home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkWm.c < prev    next >
C/C++ Source or Header  |  1995-06-28  |  115KB  |  3,762 lines

  1. /* 
  2.  * tkWm.c --
  3.  *
  4.  *    This module takes care of the interactions between a Tk-based
  5.  *    application and the window manager.  Among other things, it
  6.  *    implements the "wm" command and passes geometry information
  7.  *    to the window manager.
  8.  *
  9.  * Copyright (c) 1991-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  */
  15.  
  16. static char sccsid[] = "@(#) tkWm.c 1.105 95/06/28 17:38:53";
  17.  
  18. #include "tkPort.h"
  19. #include "tkInt.h"
  20. #include <errno.h>
  21.  
  22. /*
  23.  * A data structure of the following type holds information for
  24.  * each window manager protocol (such as WM_DELETE_WINDOW) for
  25.  * which a handler (i.e. a Tcl command) has been defined for a
  26.  * particular top-level window.
  27.  */
  28.  
  29. typedef struct ProtocolHandler {
  30.     Atom protocol;        /* Identifies the protocol. */
  31.     struct ProtocolHandler *nextPtr;
  32.                 /* Next in list of protocol handlers for
  33.                  * the same top-level window, or NULL for
  34.                  * end of list. */
  35.     Tcl_Interp *interp;        /* Interpreter in which to invoke command. */
  36.     char command[4];        /* Tcl command to invoke when a client
  37.                  * message for this protocol arrives. 
  38.                  * The actual size of the structure varies
  39.                  * to accommodate the needs of the actual
  40.                  * command. THIS MUST BE THE LAST FIELD OF
  41.                  * THE STRUCTURE. */
  42. } ProtocolHandler;
  43.  
  44. #define HANDLER_SIZE(cmdLength) \
  45.     ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
  46.  
  47. /*
  48.  * A data structure of the following type holds window-manager-related
  49.  * information for each top-level window in an application.
  50.  */
  51.  
  52. typedef struct TkWmInfo {
  53.     TkWindow *winPtr;        /* Pointer to main Tk information for
  54.                  * this window. */
  55.     Window reparent;        /* If the window has been reparented, this
  56.                  * gives the ID of the ancestor of the window
  57.                  * that is a child of the root window (may
  58.                  * not be window's immediate parent).  If
  59.                  * the window isn't reparented, this has the
  60.                  * value None. */
  61.     Tk_Uid titleUid;        /* Title to display in window caption.  If
  62.                  * NULL, use name of widget. */
  63.     Tk_Uid iconName;        /* Name to display in icon. */
  64.     Window master;        /* Master window for TRANSIENT_FOR property,
  65.                  * or None. */
  66.     XWMHints hints;        /* Various pieces of information for
  67.                  * window manager. */
  68.     Tk_Uid leaderName;        /* Path name of leader of window group
  69.                  * (corresponds to hints.window_group).
  70.                  * Note:  this field doesn't get updated
  71.                  * if leader is destroyed. */
  72.     Tk_Uid masterWindowName;    /* Path name of window specified as master
  73.                  * in "wm transient" command, or NULL.
  74.                  * Note:  this field doesn't get updated if
  75.                  * masterWindowName is destroyed. */
  76.     Tk_Window icon;        /* Window to use as icon for this window,
  77.                  * or NULL. */
  78.     Tk_Window iconFor;        /* Window for which this window is icon, or
  79.                  * NULL if this isn't an icon for anyone. */
  80.     int withdrawn;        /* Non-zero means window has been withdrawn. */
  81.  
  82.     /*
  83.      * Information used to construct an XSizeHints structure for
  84.      * the window manager:
  85.      */
  86.  
  87.     int sizeHintsFlags;        /* Flags word for XSizeHints structure.
  88.                  * If the PBaseSize flag is set then the
  89.                  * window is gridded;  otherwise it isn't
  90.                  * gridded. */
  91.     int minWidth, minHeight;    /* Minimum dimensions of window, in
  92.                  * grid units, not pixels. */
  93.     int maxWidth, maxHeight;    /* Maximum dimensions of window, in
  94.                  * grid units, not pixels. */
  95.     Tk_Window gridWin;        /* Identifies the window that controls
  96.                  * gridding for this top-level, or NULL if
  97.                  * the top-level isn't currently gridded. */
  98.     int widthInc, heightInc;    /* Increments for size changes (# pixels
  99.                  * per step). */
  100.     struct {
  101.     int x;    /* numerator */
  102.     int y;  /* denominator */
  103.     } minAspect, maxAspect;    /* Min/max aspect ratios for window. */
  104.     int reqGridWidth, reqGridHeight;
  105.                 /* The dimensions of the window (in
  106.                  * grid units) requested through
  107.                  * the geometry manager. */
  108.     int gravity;        /* Desired window gravity. */
  109.  
  110.     /*
  111.      * Information used to manage the size and location of a window.
  112.      */
  113.  
  114.     int width, height;        /* Desired dimensions of window, specified
  115.                  * in grid units.  These values are
  116.                  * set by the "wm geometry" command and by
  117.                  * ConfigureNotify events (for when wm
  118.                  * resizes window).  -1 means user hasn't
  119.                  * requested dimensions. */
  120.     int x, y;            /* Desired X and Y coordinates for window.
  121.                  * These values are set by "wm geometry",
  122.                  * plus by ConfigureNotify events (when wm
  123.                  * moves window).  These numbers are
  124.                  * different than the numbers stored in
  125.                  * winPtr->changes because (a) they could be
  126.                  * measured from the right or bottom edge
  127.                  * of the screen (see WM_NEGATIVE_X and
  128.                  * WM_NEGATIVE_Y flags) and (b) if the window
  129.                  * has been reparented then they refer to the
  130.                  * parent rather than the window itself. */
  131.     int parentWidth, parentHeight;
  132.                 /* Width and height of reparent, in pixels
  133.                  * *including border*.  If window hasn't been
  134.                  * reparented then these will be the outer
  135.                  * dimensions of the window, including
  136.                  * border. */
  137.     int xInParent, yInParent;    /* Offset of window within reparent,  measured
  138.                  * from upper-left outer corner of parent's
  139.                  * border to upper-left outer corner of child's
  140.                  * border.  If not reparented then these are
  141.                  * zero. */
  142.     int configWidth, configHeight;
  143.                 /* Dimensions passed to last request that we
  144.                  * issued to change geometry of window.  Used
  145.                  * to eliminate redundant resize operations. */
  146.  
  147.     /*
  148.      * Information about the virtual root window for this top-level,
  149.      * if there is one.
  150.      */
  151.  
  152.     Window vRoot;        /* Virtual root window for this top-level,
  153.                  * or None if there is no virtual root
  154.                  * window (i.e. just use the screen's root). */
  155.     int vRootX, vRootY;        /* Position of the virtual root inside the
  156.                  * root window.  If the WM_VROOT_OFFSET_STALE
  157.                  * flag is set then this information may be
  158.                  * incorrect and needs to be refreshed from
  159.                  * the X server.  If vRoot is None then these
  160.                  * values are both 0. */
  161.     int vRootWidth, vRootHeight;/* Dimensions of the virtual root window.
  162.                  * If vRoot is None, gives the dimensions
  163.                  * of the containing screen.  This information
  164.                  * is never stale, even though vRootX and
  165.                  * vRootY can be. */
  166.  
  167.     /*
  168.      * Miscellaneous information.
  169.      */
  170.  
  171.     ProtocolHandler *protPtr;    /* First in list of protocol handlers for
  172.                  * this window (NULL means none). */
  173.     int cmdArgc;        /* Number of elements in cmdArgv below. */
  174.     char **cmdArgv;        /* Array of strings to store in the
  175.                  * WM_COMMAND property.  NULL means nothing
  176.                  * available. */
  177.     char *clientMachine;    /* String to store in WM_CLIENT_MACHINE
  178.                  * property, or NULL. */
  179.     int flags;            /* Miscellaneous flags, defined below. */
  180.     struct TkWmInfo *nextPtr;    /* Next in list of all top-level windows. */
  181. } WmInfo;
  182.  
  183. /*
  184.  * Flag values for WmInfo structures:
  185.  *
  186.  * WM_NEVER_MAPPED -        non-zero means window has never been
  187.  *                mapped;  need to update all info when
  188.  *                window is first mapped.
  189.  * WM_UPDATE_PENDING -        non-zero means a call to UpdateGeometryInfo
  190.  *                has already been scheduled for this
  191.  *                window;  no need to schedule another one.
  192.  * WM_NEGATIVE_X -        non-zero means x-coordinate is measured in
  193.  *                pixels from right edge of screen, rather
  194.  *                than from left edge.
  195.  * WM_NEGATIVE_Y -        non-zero means y-coordinate is measured in
  196.  *                pixels up from bottom of screen, rather than
  197.  *                down from top.
  198.  * WM_UPDATE_SIZE_HINTS -    non-zero means that new size hints need to be
  199.  *                propagated to window manager.
  200.  * WM_SYNC_PENDING -        set to non-zero while waiting for the window
  201.  *                manager to respond to some state change.
  202.  * WM_VROOT_OFFSET_STALE -    non-zero means that (x,y) offset information
  203.  *                about the virtual root window is stale and
  204.  *                needs to be fetched fresh from the X server.
  205.  * WM_ABOUT_TO_MAP -        non-zero means that the window is about to
  206.  *                be mapped by TkWmMapWindow.  This is used
  207.  *                by UpdateGeometryInfo to modify its behavior.
  208.  * WM_MOVE_PENDING -        non-zero means the application has requested
  209.  *                a new position for the window, but it hasn't
  210.  *                been reflected through the window manager
  211.  *                yet.
  212.  * WM_COLORMAPS_EXPLICIT -    non-zero means the colormap windows were
  213.  *                set explicitly via "wm colormapwindows".
  214.  * WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"
  215.  *                was called the top-level itself wasn't
  216.  *                specified, so we added it implicitly at
  217.  *                the end of the list.
  218.  * WM_WIDTH_NOT_RESIZABLE -    non-zero means that we're not supposed to
  219.  *                allow the user to change the width of the
  220.  *                window (controlled by "wm resizable"
  221.  *                command).
  222.  * WM_HEIGHT_NOT_RESIZABLE -    non-zero means that we're not supposed to
  223.  *                allow the user to change the height of the
  224.  *                window (controlled by "wm resizable"
  225.  *                command).
  226.  */
  227.  
  228. #define WM_NEVER_MAPPED            1
  229. #define WM_UPDATE_PENDING        2
  230. #define WM_NEGATIVE_X            4
  231. #define WM_NEGATIVE_Y            8
  232. #define WM_UPDATE_SIZE_HINTS        0x10
  233. #define WM_SYNC_PENDING            0x20
  234. #define WM_VROOT_OFFSET_STALE        0x40
  235. #define WM_ABOUT_TO_MAP            0x100
  236. #define WM_MOVE_PENDING            0x200
  237. #define WM_COLORMAPS_EXPLICIT        0x400
  238. #define WM_ADDED_TOPLEVEL_COLORMAP    0x800
  239. #define WM_WIDTH_NOT_RESIZABLE        0x1000
  240. #define WM_HEIGHT_NOT_RESIZABLE        0x2000
  241.  
  242. /*
  243.  * This module keeps a list of all top-level windows, primarily to
  244.  * simplify the job of Tk_CoordsToWindow.
  245.  */
  246.  
  247. static WmInfo *firstWmPtr = NULL;    /* Points to first top-level window. */
  248.  
  249.  
  250. /*
  251.  * The variable below is used to enable or disable tracing in this
  252.  * module.  If tracing is enabled, then information is printed on
  253.  * standard output about interesting interactions with the window
  254.  * manager.
  255.  */
  256.  
  257. static int wmTracing = 0;
  258.  
  259. /*
  260.  * The following structure is the official type record for geometry
  261.  * management of top-level windows.
  262.  */
  263.  
  264. static void        TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
  265.                 Tk_Window tkwin));
  266.  
  267. static Tk_GeomMgr wmMgrType = {
  268.     "wm",                /* name */
  269.     TopLevelReqProc,            /* requestProc */
  270.     (Tk_GeomLostSlaveProc *) NULL,    /* lostSlaveProc */
  271. };
  272.  
  273. /*
  274.  * Forward declarations for procedures defined in this file:
  275.  */
  276.  
  277. static int        ComputeReparentGeometry _ANSI_ARGS_((TkWindow *winPtr));
  278. static void        ConfigureEvent _ANSI_ARGS_((TkWindow *winPtr,
  279.                 XConfigureEvent *eventPtr));
  280. static int        ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
  281.                 char *string, TkWindow *winPtr));
  282. static void        ReparentEvent _ANSI_ARGS_((TkWindow *winPtr,
  283.                 XReparentEvent *eventPtr));
  284. static void        TopLevelEventProc _ANSI_ARGS_((ClientData clientData,
  285.                 XEvent *eventPtr));
  286. static void        TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
  287.                 Tk_Window tkwin));
  288. static void        UpdateGeometryInfo _ANSI_ARGS_((
  289.                 ClientData clientData));
  290. static void        UpdateHints _ANSI_ARGS_((TkWindow *winPtr));
  291. static void        UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));
  292. static void        UpdateVRootGeometry _ANSI_ARGS_((WmInfo *wmPtr));
  293. static void        UpdateWmProtocols _ANSI_ARGS_((WmInfo *wmPtr));
  294. static void        WaitForConfigureNotify _ANSI_ARGS_((TkWindow *winPtr,
  295.                 unsigned long serial));
  296. static int        WaitForEvent _ANSI_ARGS_((Display *display,
  297.                 Window window, long mask, XEvent *eventPtr));
  298. static void        WaitForMapNotify _ANSI_ARGS_((TkWindow *winPtr,
  299.                 int mapped));
  300.  
  301. /*
  302.  *--------------------------------------------------------------
  303.  *
  304.  * TkWmNewWindow --
  305.  *
  306.  *    This procedure is invoked whenever a new top-level
  307.  *    window is created.  Its job is to initialize the WmInfo
  308.  *    structure for the window.
  309.  *
  310.  * Results:
  311.  *    None.
  312.  *
  313.  * Side effects:
  314.  *    A WmInfo structure gets allocated and initialized.
  315.  *
  316.  *--------------------------------------------------------------
  317.  */
  318.  
  319. void
  320. TkWmNewWindow(winPtr)
  321.     TkWindow *winPtr;        /* Newly-created top-level window. */
  322. {
  323.     register WmInfo *wmPtr;
  324.  
  325.     wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
  326.     wmPtr->winPtr = winPtr;
  327.     wmPtr->reparent = None;
  328.     wmPtr->titleUid = NULL;
  329.     wmPtr->iconName = NULL;
  330.     wmPtr->master = None;
  331.     wmPtr->hints.flags = InputHint | StateHint;
  332.     wmPtr->hints.input = True;
  333.     wmPtr->hints.initial_state = NormalState;
  334.     wmPtr->hints.icon_pixmap = None;
  335.     wmPtr->hints.icon_window = None;
  336.     wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
  337.     wmPtr->hints.icon_mask = None;
  338.     wmPtr->hints.window_group = None;
  339.     wmPtr->leaderName = NULL;
  340.     wmPtr->masterWindowName = NULL;
  341.     wmPtr->icon = NULL;
  342.     wmPtr->iconFor = NULL;
  343.     wmPtr->withdrawn = 0;
  344.     wmPtr->sizeHintsFlags = 0;
  345.     wmPtr->minWidth = wmPtr->minHeight = 1;
  346.  
  347.     /*
  348.      * Default the maximum dimensions to the size of the display, minus
  349.      * a guess about how space is needed for window manager decorations.
  350.      */
  351.  
  352.     wmPtr->maxWidth = DisplayWidth(winPtr->display, winPtr->screenNum) - 15;
  353.     wmPtr->maxHeight = DisplayHeight(winPtr->display, winPtr->screenNum) - 30;
  354.     wmPtr->gridWin = NULL;
  355.     wmPtr->widthInc = wmPtr->heightInc = 1;
  356.     wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
  357.     wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
  358.     wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
  359.     wmPtr->gravity = NorthWestGravity;
  360.     wmPtr->width = -1;
  361.     wmPtr->height = -1;
  362.     wmPtr->x = winPtr->changes.x;
  363.     wmPtr->y = winPtr->changes.y;
  364.     wmPtr->parentWidth = winPtr->changes.width
  365.         + 2*winPtr->changes.border_width;
  366.     wmPtr->parentHeight = winPtr->changes.height
  367.         + 2*winPtr->changes.border_width;
  368.     wmPtr->xInParent = wmPtr->yInParent = 0;
  369.     wmPtr->configWidth = -1;
  370.     wmPtr->configHeight = -1;
  371.     wmPtr->vRoot = None;
  372.     wmPtr->protPtr = NULL;
  373.     wmPtr->cmdArgv = NULL;
  374.     wmPtr->clientMachine = NULL;
  375.     wmPtr->flags = WM_NEVER_MAPPED;
  376.     wmPtr->nextPtr = firstWmPtr;
  377.     firstWmPtr = wmPtr;
  378.     winPtr->wmInfoPtr = wmPtr;
  379.  
  380.     UpdateVRootGeometry(wmPtr);
  381.  
  382.     /*
  383.      * Tk must monitor structure events for top-level windows, in order
  384.      * to detect size and position changes caused by window managers.
  385.      */
  386.  
  387.     Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask,
  388.         TopLevelEventProc, (ClientData) winPtr);
  389.  
  390.     /*
  391.      * Arrange for geometry requests to be reflected from the window
  392.      * to the window manager.
  393.      */
  394.  
  395.     Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
  396. }
  397.  
  398. /*
  399.  *--------------------------------------------------------------
  400.  *
  401.  * TkWmMapWindow --
  402.  *
  403.  *    This procedure is invoked to map a top-level window.  This
  404.  *    module gets a chance to update all window-manager-related
  405.  *    information in properties before the window manager sees
  406.  *    the map event and checks the properties.  It also gets to
  407.  *    decide whether or not to even map the window after all.
  408.  *
  409.  * Results:
  410.  *    None.
  411.  *
  412.  * Side effects:
  413.  *    Properties of winPtr may get updated to provide up-to-date
  414.  *    information to the window manager.  The window may also get
  415.  *    mapped, but it may not be if this procedure decides that
  416.  *    isn't appropriate (e.g. because the window is withdrawn).
  417.  *
  418.  *--------------------------------------------------------------
  419.  */
  420.  
  421. void
  422. TkWmMapWindow(winPtr)
  423.     TkWindow *winPtr;        /* Top-level window that's about to
  424.                  * be mapped. */
  425. {
  426.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  427.     XTextProperty textProp;
  428.  
  429.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  430.     wmPtr->flags &= ~WM_NEVER_MAPPED;
  431.  
  432.     /*
  433.      * This is the first time this window has ever been mapped.
  434.      * Store all the window-manager-related information for the
  435.      * window.
  436.      */
  437.  
  438.     if (wmPtr->titleUid == NULL) {
  439.         wmPtr->titleUid = winPtr->nameUid;
  440.     }
  441.     if (XStringListToTextProperty(&wmPtr->titleUid, 1, &textProp)  != 0) {
  442.         XSetWMName(winPtr->display, winPtr->window, &textProp);
  443.         XFree((char *) textProp.value);
  444.     }
  445.     
  446.     TkWmSetClass(winPtr);
  447.     
  448.     if (wmPtr->iconName != NULL) {
  449.         XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
  450.     }
  451.     
  452.     if (wmPtr->master != None) {
  453.         XSetTransientForHint(winPtr->display, winPtr->window,
  454.             wmPtr->master);
  455.     }
  456.     
  457.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  458.     UpdateHints(winPtr);
  459.     UpdateWmProtocols(wmPtr);
  460.     if (wmPtr->cmdArgv != NULL) {
  461.         XSetCommand(winPtr->display, winPtr->window, wmPtr->cmdArgv,
  462.             wmPtr->cmdArgc);
  463.     }
  464.     if (wmPtr->clientMachine != NULL) {
  465.         if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
  466.             != 0) {
  467.         XSetWMClientMachine(winPtr->display, winPtr->window,
  468.             &textProp);
  469.         XFree((char *) textProp.value);
  470.         }
  471.     }
  472.     }
  473.     if (wmPtr->hints.initial_state == WithdrawnState) {
  474.     return;
  475.     }
  476.     if (wmPtr->iconFor != NULL) {
  477.     /*
  478.      * This window is an icon for somebody else.  Make sure that
  479.      * the geometry is up-to-date, then return without mapping
  480.      * the window.
  481.      */
  482.  
  483.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  484.         Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  485.     }
  486.     UpdateGeometryInfo((ClientData) winPtr);
  487.     return;
  488.     }
  489.     wmPtr->flags |= WM_ABOUT_TO_MAP;
  490.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  491.     Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  492.     }
  493.     UpdateGeometryInfo((ClientData) winPtr);
  494.     wmPtr->flags &= ~WM_ABOUT_TO_MAP;
  495.  
  496.     /*
  497.      * Map the window, then wait to be sure that the window manager has
  498.      * processed the map operation.
  499.      */
  500.  
  501.     XMapWindow(winPtr->display, winPtr->window);
  502.     if (wmPtr->hints.initial_state == NormalState) {
  503.     WaitForMapNotify(winPtr, 1);
  504.     }
  505. }
  506.  
  507. /*
  508.  *--------------------------------------------------------------
  509.  *
  510.  * TkWmUnmapWindow --
  511.  *
  512.  *    This procedure is invoked to unmap a top-level window.  The
  513.  *    only thing it does special is to wait for the window actually
  514.  *    to be unmapped.
  515.  *
  516.  * Results:
  517.  *    None.
  518.  *
  519.  * Side effects:
  520.  *    Unmaps the window.
  521.  *
  522.  *--------------------------------------------------------------
  523.  */
  524.  
  525. void
  526. TkWmUnmapWindow(winPtr)
  527.     TkWindow *winPtr;        /* Top-level window that's about to
  528.                  * be mapped. */
  529. {
  530.     /*
  531.      * It seems to be important to wait after unmapping a top-level
  532.      * window until the window really gets unmapped.  I don't completely
  533.      * understand all the interactions with the window manager, but if
  534.      * we go on without waiting, and if the window is then mapped again
  535.      * quickly, events seem to get lost so that we think the window isn't
  536.      * mapped when in fact it is mapped.  I suspect that this has something
  537.      * to do with the window manager filtering Map events (and possily not
  538.      * filtering Unmap events?).
  539.      */ 
  540.     XUnmapWindow(winPtr->display, winPtr->window);
  541.     WaitForMapNotify(winPtr, 0);
  542. }
  543.  
  544. /*
  545.  *--------------------------------------------------------------
  546.  *
  547.  * TkWmDeadWindow --
  548.  *
  549.  *    This procedure is invoked when a top-level window is
  550.  *    about to be deleted.  It cleans up the wm-related data
  551.  *    structures for the window.
  552.  *
  553.  * Results:
  554.  *    None.
  555.  *
  556.  * Side effects:
  557.  *    The WmInfo structure for winPtr gets freed up.
  558.  *
  559.  *--------------------------------------------------------------
  560.  */
  561.  
  562. void
  563. TkWmDeadWindow(winPtr)
  564.     TkWindow *winPtr;        /* Top-level window that's being deleted. */
  565. {
  566.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  567.     WmInfo *wmPtr2;
  568.  
  569.     if (wmPtr == NULL) {
  570.     return;
  571.     }
  572.     if (firstWmPtr == wmPtr) {
  573.     firstWmPtr = wmPtr->nextPtr;
  574.     } else {
  575.     register WmInfo *prevPtr;
  576.  
  577.     for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {
  578.         if (prevPtr == NULL) {
  579.         panic("couldn't unlink window in TkWmDeadWindow");
  580.         }
  581.         if (prevPtr->nextPtr == wmPtr) {
  582.         prevPtr->nextPtr = wmPtr->nextPtr;
  583.         break;
  584.         }
  585.     }
  586.     }
  587.     if (wmPtr->hints.flags & IconPixmapHint) {
  588.     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
  589.     }
  590.     if (wmPtr->hints.flags & IconMaskHint) {
  591.     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
  592.     }
  593.     if (wmPtr->icon != NULL) {
  594.     wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
  595.     wmPtr2->iconFor = NULL;
  596.     wmPtr2->withdrawn = 1;
  597.     }
  598.     if (wmPtr->iconFor != NULL) {
  599.     wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
  600.     wmPtr2->icon = NULL;
  601.     wmPtr2->hints.flags &= ~IconWindowHint;
  602.     UpdateHints((TkWindow *) wmPtr->iconFor);
  603.     }
  604.     while (wmPtr->protPtr != NULL) {
  605.     ProtocolHandler *protPtr;
  606.  
  607.     protPtr = wmPtr->protPtr;
  608.     wmPtr->protPtr = protPtr->nextPtr;
  609.     Tk_EventuallyFree((ClientData) protPtr, (Tk_FreeProc *) free);
  610.     }
  611.     if (wmPtr->cmdArgv != NULL) {
  612.     ckfree((char *) wmPtr->cmdArgv);
  613.     }
  614.     if (wmPtr->clientMachine != NULL) {
  615.     ckfree((char *) wmPtr->clientMachine);
  616.     }
  617.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  618.     Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  619.     }
  620.     ckfree((char *) wmPtr);
  621.     winPtr->wmInfoPtr = NULL;
  622. }
  623.  
  624. /*
  625.  *--------------------------------------------------------------
  626.  *
  627.  * TkWmSetClass --
  628.  *
  629.  *    This procedure is invoked whenever a top-level window's
  630.  *    class is changed.  If the window has been mapped then this
  631.  *    procedure updates the window manager property for the
  632.  *    class.  If the window hasn't been mapped, the update is
  633.  *    deferred until just before the first mapping.
  634.  *
  635.  * Results:
  636.  *    None.
  637.  *
  638.  * Side effects:
  639.  *    A window property may get updated.
  640.  *
  641.  *--------------------------------------------------------------
  642.  */
  643.  
  644. void
  645. TkWmSetClass(winPtr)
  646.     TkWindow *winPtr;        /* Newly-created top-level window. */
  647. {
  648.     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  649.     return;
  650.     }
  651.  
  652.     if (winPtr->classUid != NULL) {
  653.     XClassHint *classPtr;
  654.  
  655.     classPtr = XAllocClassHint();
  656.     classPtr->res_name = winPtr->nameUid;
  657.     classPtr->res_class = winPtr->classUid;
  658.     XSetClassHint(winPtr->display, winPtr->window, classPtr);
  659.     XFree((char *) classPtr);
  660.     }
  661. }
  662.  
  663. /*
  664.  *----------------------------------------------------------------------
  665.  *
  666.  * Tk_WmCmd --
  667.  *
  668.  *    This procedure is invoked to process the "wm" Tcl command.
  669.  *    See the user documentation for details on what it does.
  670.  *
  671.  * Results:
  672.  *    A standard Tcl result.
  673.  *
  674.  * Side effects:
  675.  *    See the user documentation.
  676.  *
  677.  *----------------------------------------------------------------------
  678.  */
  679.  
  680.     /* ARGSUSED */
  681. int
  682. Tk_WmCmd(clientData, interp, argc, argv)
  683.     ClientData clientData;    /* Main window associated with
  684.                  * interpreter. */
  685.     Tcl_Interp *interp;        /* Current interpreter. */
  686.     int argc;            /* Number of arguments. */
  687.     char **argv;        /* Argument strings. */
  688. {
  689.     Tk_Window tkwin = (Tk_Window) clientData;
  690.     TkWindow *winPtr;
  691.     register WmInfo *wmPtr;
  692.     int c;
  693.     size_t length;
  694.  
  695.     if (argc < 2) {
  696.     wrongNumArgs:
  697.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  698.         argv[0], " option window ?arg ...?\"", (char *) NULL);
  699.     return TCL_ERROR;
  700.     }
  701.     c = argv[1][0];
  702.     length = strlen(argv[1]);
  703.     if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)
  704.         && (length >= 3)) {
  705.     if ((argc != 2) && (argc != 3)) {
  706.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  707.             argv[0], " tracing ?boolean?\"", (char *) NULL);
  708.         return TCL_ERROR;
  709.     }
  710.     if (argc == 2) {
  711.         interp->result = (wmTracing) ? "on" : "off";
  712.         return TCL_OK;
  713.     }
  714.     return Tcl_GetBoolean(interp, argv[2], &wmTracing);
  715.     }
  716.  
  717.     if (argc < 3) {
  718.     goto wrongNumArgs;
  719.     }
  720.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  721.     if (winPtr == NULL) {
  722.     return TCL_ERROR;
  723.     }
  724.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  725.     Tcl_AppendResult(interp, "window \"", winPtr->pathName,
  726.         "\" isn't a top-level window", (char *) NULL);
  727.     return TCL_ERROR;
  728.     }
  729.     wmPtr = winPtr->wmInfoPtr;
  730.     if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
  731.     int numer1, denom1, numer2, denom2;
  732.  
  733.     if ((argc != 3) && (argc != 7)) {
  734.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  735.             argv[0], " aspect window ?minNumer minDenom ",
  736.             "maxNumer maxDenom?\"", (char *) NULL);
  737.         return TCL_ERROR;
  738.     }
  739.     if (argc == 3) {
  740.         if (wmPtr->sizeHintsFlags & PAspect) {
  741.         sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
  742.             wmPtr->minAspect.y, wmPtr->maxAspect.x,
  743.             wmPtr->maxAspect.y);
  744.         }
  745.         return TCL_OK;
  746.     }
  747.     if (*argv[3] == '\0') {
  748.         wmPtr->sizeHintsFlags &= ~PAspect;
  749.     } else {
  750.         if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
  751.             || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
  752.             || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
  753.             || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
  754.         return TCL_ERROR;
  755.         }
  756.         if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
  757.             (denom2 <= 0)) {
  758.         interp->result = "aspect number can't be <= 0";
  759.         return TCL_ERROR;
  760.         }
  761.         wmPtr->minAspect.x = numer1;
  762.         wmPtr->minAspect.y = denom1;
  763.         wmPtr->maxAspect.x = numer2;
  764.         wmPtr->maxAspect.y = denom2;
  765.         wmPtr->sizeHintsFlags |= PAspect;
  766.     }
  767.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  768.     goto updateGeom;
  769.     } else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)
  770.         && (length >= 2)) {
  771.     if ((argc != 3) && (argc != 4)) {
  772.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  773.             argv[0], " client window ?name?\"",
  774.             (char *) NULL);
  775.         return TCL_ERROR;
  776.     }
  777.     if (argc == 3) {
  778.         if (wmPtr->clientMachine != NULL) {
  779.         interp->result = wmPtr->clientMachine;
  780.         }
  781.         return TCL_OK;
  782.     }
  783.     if (argv[3][0] == 0) {
  784.         if (wmPtr->clientMachine != NULL) {
  785.         ckfree((char *) wmPtr->clientMachine);
  786.         wmPtr->clientMachine = NULL;
  787.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  788.             XDeleteProperty(winPtr->display, winPtr->window,
  789.                 Tk_InternAtom((Tk_Window) winPtr,
  790.                 "WM_CLIENT_MACHINE"));
  791.         }
  792.         }
  793.         return TCL_OK;
  794.     }
  795.     if (wmPtr->clientMachine != NULL) {
  796.         ckfree((char *) wmPtr->clientMachine);
  797.     }
  798.     wmPtr->clientMachine = (char *)
  799.         ckalloc((unsigned) (strlen(argv[3]) + 1));
  800.     strcpy(wmPtr->clientMachine, argv[3]);
  801.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  802.         XTextProperty textProp;
  803.         if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
  804.             != 0) {
  805.         XSetWMClientMachine(winPtr->display, winPtr->window,
  806.             &textProp);
  807.         XFree((char *) textProp.value);
  808.         }
  809.     }
  810.     } else if ((c == 'c') && (strncmp(argv[1], "colormapwindows", length) == 0)
  811.         && (length >= 3)) {
  812.     Window *cmapList;
  813.     TkWindow *winPtr2;
  814.     int count, i, windowArgc, gotToplevel;
  815.     char buffer[20], **windowArgv;
  816.  
  817.     if ((argc != 3) && (argc != 4)) {
  818.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  819.             argv[0], " colormapwindows window ?windowList?\"",
  820.             (char *) NULL);
  821.         return TCL_ERROR;
  822.     }
  823.     if (argc == 3) {
  824.         Tk_MakeWindowExist((Tk_Window) winPtr);
  825.         if (XGetWMColormapWindows(winPtr->display, winPtr->window,
  826.             &cmapList, &count) == 0) {
  827.         return TCL_OK;
  828.         }
  829.         for (i = 0; i < count; i++) {
  830.         if ((i == (count-1))
  831.             && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
  832.             break;
  833.         }
  834.             winPtr2  = (TkWindow *) Tk_IdToWindow(winPtr->display,
  835.             cmapList[i]);
  836.         if (winPtr2 == NULL) {
  837.             sprintf(buffer, "0x%lx", cmapList[i]);
  838.             Tcl_AppendElement(interp, buffer);
  839.         } else {
  840.             Tcl_AppendElement(interp, winPtr2->pathName);
  841.         }
  842.         }
  843.         return TCL_OK;
  844.     }
  845.     if (Tcl_SplitList(interp, argv[3], &windowArgc, &windowArgv)
  846.         != TCL_OK) {
  847.         return TCL_ERROR;
  848.     }
  849.     cmapList = (Window *) ckalloc((unsigned)
  850.         ((windowArgc+1)*sizeof(Window)));
  851.     gotToplevel = 0;
  852.     for (i = 0; i < windowArgc; i++) {
  853.         winPtr2 = (TkWindow *) Tk_NameToWindow(interp, windowArgv[i],
  854.             tkwin);
  855.         if (winPtr2 == NULL) {
  856.         ckfree((char *) cmapList);
  857.         ckfree((char *) windowArgv);
  858.         return TCL_ERROR;
  859.         }
  860.         if (winPtr2 == winPtr) {
  861.         gotToplevel = 1;
  862.         }
  863.         if (winPtr2->window == None) {
  864.         Tk_MakeWindowExist((Tk_Window) winPtr2);
  865.         }
  866.         cmapList[i] = winPtr2->window;
  867.     }
  868.     if (!gotToplevel) {
  869.         wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
  870.         cmapList[windowArgc] = winPtr->window;
  871.         windowArgc++;
  872.     } else {
  873.         wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
  874.     }
  875.     wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
  876.     XSetWMColormapWindows(winPtr->display, winPtr->window, cmapList,
  877.         windowArgc);
  878.     ckfree((char *) cmapList);
  879.     ckfree((char *) windowArgv);
  880.     return TCL_OK;
  881.     } else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)
  882.         && (length >= 3)) {
  883.     int cmdArgc;
  884.     char **cmdArgv;
  885.  
  886.     if ((argc != 3) && (argc != 4)) {
  887.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  888.             argv[0], " command window ?value?\"",
  889.             (char *) NULL);
  890.         return TCL_ERROR;
  891.     }
  892.     if (argc == 3) {
  893.         if (wmPtr->cmdArgv != NULL) {
  894.         interp->result = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv);
  895.         interp->freeProc = (Tcl_FreeProc *) free;
  896.         }
  897.         return TCL_OK;
  898.     }
  899.     if (argv[3][0] == 0) {
  900.         if (wmPtr->cmdArgv != NULL) {
  901.         ckfree((char *) wmPtr->cmdArgv);
  902.         wmPtr->cmdArgv = NULL;
  903.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  904.             XDeleteProperty(winPtr->display, winPtr->window,
  905.                 Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
  906.         }
  907.         }
  908.         return TCL_OK;
  909.     }
  910.     if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {
  911.         return TCL_ERROR;
  912.     }
  913.     if (wmPtr->cmdArgv != NULL) {
  914.         ckfree((char *) wmPtr->cmdArgv);
  915.     }
  916.     wmPtr->cmdArgc = cmdArgc;
  917.     wmPtr->cmdArgv = cmdArgv;
  918.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  919.         XSetCommand(winPtr->display, winPtr->window, cmdArgv, cmdArgc);
  920.     }
  921.     } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
  922.     if (argc != 3) {
  923.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  924.             argv[0], " deiconify window\"", (char *) NULL);
  925.         return TCL_ERROR;
  926.     }
  927.     if (wmPtr->iconFor != NULL) {
  928.         Tcl_AppendResult(interp, "can't deiconify ", argv[2],
  929.             ": it is an icon for ", winPtr->pathName, (char *) NULL);
  930.         return TCL_ERROR;
  931.     }
  932.     wmPtr->hints.initial_state = NormalState;
  933.     wmPtr->withdrawn = 0;
  934.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  935.         return TCL_OK;
  936.     }
  937.     UpdateHints(winPtr);
  938.     Tk_MapWindow((Tk_Window) winPtr);
  939.     } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)
  940.         && (length >= 2)) {
  941.     if ((argc != 3) && (argc != 4)) {
  942.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  943.             argv[0], " focusmodel window ?active|passive?\"",
  944.             (char *) NULL);
  945.         return TCL_ERROR;
  946.     }
  947.     if (argc == 3) {
  948.         interp->result = wmPtr->hints.input ? "passive" : "active";
  949.         return TCL_OK;
  950.     }
  951.     c = argv[3][0];
  952.     length = strlen(argv[3]);
  953.     if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
  954.         wmPtr->hints.input = False;
  955.     } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
  956.         wmPtr->hints.input = True;
  957.     } else {
  958.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  959.             "\": must be active or passive", (char *) NULL);
  960.         return TCL_ERROR;
  961.     }
  962.     UpdateHints(winPtr);
  963.     } else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)
  964.         && (length >= 2)) {
  965.     Window window;
  966.  
  967.     if (argc != 3) {
  968.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  969.             argv[0], " frame window\"", (char *) NULL);
  970.         return TCL_ERROR;
  971.     }
  972.     window = wmPtr->reparent;
  973.     if (window == None) {
  974.         window = Tk_WindowId((Tk_Window) winPtr);
  975.     }
  976.     sprintf(interp->result, "0x%x", (unsigned int) window);
  977.     } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
  978.         && (length >= 2)) {
  979.     char xSign, ySign;
  980.     int width, height;
  981.  
  982.     if ((argc != 3) && (argc != 4)) {
  983.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  984.             argv[0], " geometry window ?newGeometry?\"",
  985.             (char *) NULL);
  986.         return TCL_ERROR;
  987.     }
  988.     if (argc == 3) {
  989.         xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
  990.         ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
  991.         if (wmPtr->gridWin != NULL) {
  992.         width = wmPtr->reqGridWidth + (winPtr->changes.width
  993.             - winPtr->reqWidth)/wmPtr->widthInc;
  994.         height = wmPtr->reqGridHeight + (winPtr->changes.height
  995.             - winPtr->reqHeight)/wmPtr->heightInc;
  996.         } else {
  997.         width = winPtr->changes.width;
  998.         height = winPtr->changes.height;
  999.         }
  1000.         sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
  1001.             xSign, wmPtr->x, ySign, wmPtr->y);
  1002.         return TCL_OK;
  1003.     }
  1004.     if (*argv[3] == '\0') {
  1005.         wmPtr->width = -1;
  1006.         wmPtr->height = -1;
  1007.         goto updateGeom;
  1008.     }
  1009.     return ParseGeometry(interp, argv[3], winPtr);
  1010.     } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
  1011.         && (length >= 3)) {
  1012.     int reqWidth, reqHeight, widthInc, heightInc;
  1013.  
  1014.     if ((argc != 3) && (argc != 7)) {
  1015.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1016.             argv[0], " grid window ?baseWidth baseHeight ",
  1017.             "widthInc heightInc?\"", (char *) NULL);
  1018.         return TCL_ERROR;
  1019.     }
  1020.     if (argc == 3) {
  1021.         if (wmPtr->sizeHintsFlags & PBaseSize) {
  1022.         sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
  1023.             wmPtr->reqGridHeight, wmPtr->widthInc,
  1024.             wmPtr->heightInc);
  1025.         }
  1026.         return TCL_OK;
  1027.     }
  1028.     if (*argv[3] == '\0') {
  1029.         /*
  1030.          * Turn off gridding and reset the width and height
  1031.          * to make sense as ungridded numbers.
  1032.          */
  1033.  
  1034.         wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
  1035.         if (wmPtr->width != -1) {
  1036.         wmPtr->width = winPtr->reqWidth + (wmPtr->width
  1037.             - wmPtr->reqGridWidth)*wmPtr->widthInc;
  1038.         wmPtr->height = winPtr->reqHeight + (wmPtr->height
  1039.             - wmPtr->reqGridHeight)*wmPtr->heightInc;
  1040.         }
  1041.         wmPtr->widthInc = 1;
  1042.         wmPtr->heightInc = 1;
  1043.     } else {
  1044.         if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
  1045.             || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
  1046.             || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
  1047.             || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
  1048.         return TCL_ERROR;
  1049.         }
  1050.         if (reqWidth < 0) {
  1051.         interp->result = "baseWidth can't be < 0";
  1052.         return TCL_ERROR;
  1053.         }
  1054.         if (reqHeight < 0) {
  1055.         interp->result = "baseHeight can't be < 0";
  1056.         return TCL_ERROR;
  1057.         }
  1058.         if (widthInc < 0) {
  1059.         interp->result = "widthInc can't be < 0";
  1060.         return TCL_ERROR;
  1061.         }
  1062.         if (heightInc < 0) {
  1063.         interp->result = "heightInc can't be < 0";
  1064.         return TCL_ERROR;
  1065.         }
  1066.         Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
  1067.             heightInc);
  1068.     }
  1069.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1070.     goto updateGeom;
  1071.     } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
  1072.         && (length >= 3)) {
  1073.     Tk_Window tkwin2;
  1074.  
  1075.     if ((argc != 3) && (argc != 4)) {
  1076.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1077.             argv[0], " group window ?pathName?\"",
  1078.             (char *) NULL);
  1079.         return TCL_ERROR;
  1080.     }
  1081.     if (argc == 3) {
  1082.         if (wmPtr->hints.flags & WindowGroupHint) {
  1083.         interp->result = wmPtr->leaderName;
  1084.         }
  1085.         return TCL_OK;
  1086.     }
  1087.     if (*argv[3] == '\0') {
  1088.         wmPtr->hints.flags &= ~WindowGroupHint;
  1089.         wmPtr->leaderName = NULL;
  1090.     } else {
  1091.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  1092.         if (tkwin2 == NULL) {
  1093.         return TCL_ERROR;
  1094.         }
  1095.         Tk_MakeWindowExist(tkwin2);
  1096.         wmPtr->hints.window_group = Tk_WindowId(tkwin2);
  1097.         wmPtr->hints.flags |= WindowGroupHint;
  1098.         wmPtr->leaderName = Tk_PathName(tkwin2);
  1099.     }
  1100.     UpdateHints(winPtr);
  1101.     } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
  1102.         && (length >= 5)) {
  1103.     Pixmap pixmap;
  1104.  
  1105.     if ((argc != 3) && (argc != 4)) {
  1106.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1107.             argv[0], " iconbitmap window ?bitmap?\"",
  1108.             (char *) NULL);
  1109.         return TCL_ERROR;
  1110.     }
  1111.     if (argc == 3) {
  1112.         if (wmPtr->hints.flags & IconPixmapHint) {
  1113.         interp->result = Tk_NameOfBitmap(winPtr->display,
  1114.             wmPtr->hints.icon_pixmap);
  1115.         }
  1116.         return TCL_OK;
  1117.     }
  1118.     if (*argv[3] == '\0') {
  1119.         if (wmPtr->hints.icon_pixmap != None) {
  1120.         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
  1121.         wmPtr->hints.icon_pixmap = None;
  1122.         }
  1123.         wmPtr->hints.flags &= ~IconPixmapHint;
  1124.     } else {
  1125.         pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
  1126.             Tk_GetUid(argv[3]));
  1127.         if (pixmap == None) {
  1128.         return TCL_ERROR;
  1129.         }
  1130.         wmPtr->hints.icon_pixmap = pixmap;
  1131.         wmPtr->hints.flags |= IconPixmapHint;
  1132.     }
  1133.     UpdateHints(winPtr);
  1134.     } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
  1135.         && (length >= 5)) {
  1136.     if (argc != 3) {
  1137.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1138.             argv[0], " iconify window\"", (char *) NULL);
  1139.         return TCL_ERROR;
  1140.     }
  1141.     if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
  1142.         Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
  1143.             "\": override-redirect flag is set", (char *) NULL);
  1144.         return TCL_ERROR;
  1145.     }
  1146.     if (wmPtr->master != None) {
  1147.         Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
  1148.             "\": it is a transient", (char *) NULL);
  1149.         return TCL_ERROR;
  1150.     }
  1151.     if (wmPtr->iconFor != NULL) {
  1152.         Tcl_AppendResult(interp, "can't iconify ", argv[2],
  1153.             ": it is an icon for ", winPtr->pathName, (char *) NULL);
  1154.         return TCL_ERROR;
  1155.     }
  1156.     wmPtr->hints.initial_state = IconicState;
  1157.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1158.         return TCL_OK;
  1159.     }
  1160.     if (wmPtr->withdrawn) {
  1161.         UpdateHints(winPtr);
  1162.         Tk_MapWindow((Tk_Window) winPtr);
  1163.         wmPtr->withdrawn = 0;
  1164.     } else {
  1165.         if (XIconifyWindow(winPtr->display, winPtr->window,
  1166.         winPtr->screenNum) == 0) {
  1167.         interp->result =
  1168.             "couldn't send iconify message to window manager";
  1169.         return TCL_ERROR;
  1170.         }
  1171.         WaitForMapNotify(winPtr, 0);
  1172.     }
  1173.     } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
  1174.         && (length >= 5)) {
  1175.     Pixmap pixmap;
  1176.  
  1177.     if ((argc != 3) && (argc != 4)) {
  1178.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1179.             argv[0], " iconmask window ?bitmap?\"",
  1180.             (char *) NULL);
  1181.         return TCL_ERROR;
  1182.     }
  1183.     if (argc == 3) {
  1184.         if (wmPtr->hints.flags & IconMaskHint) {
  1185.         interp->result = Tk_NameOfBitmap(winPtr->display,
  1186.             wmPtr->hints.icon_mask);
  1187.         }
  1188.         return TCL_OK;
  1189.     }
  1190.     if (*argv[3] == '\0') {
  1191.         if (wmPtr->hints.icon_mask != None) {
  1192.         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
  1193.         }
  1194.         wmPtr->hints.flags &= ~IconMaskHint;
  1195.     } else {
  1196.         pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
  1197.         if (pixmap == None) {
  1198.         return TCL_ERROR;
  1199.         }
  1200.         wmPtr->hints.icon_mask = pixmap;
  1201.         wmPtr->hints.flags |= IconMaskHint;
  1202.     }
  1203.     UpdateHints(winPtr);
  1204.     } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
  1205.         && (length >= 5)) {
  1206.     if (argc > 4) {
  1207.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1208.             argv[0], " iconname window ?newName?\"", (char *) NULL);
  1209.         return TCL_ERROR;
  1210.     }
  1211.     if (argc == 3) {
  1212.         interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
  1213.         return TCL_OK;
  1214.     } else {
  1215.         wmPtr->iconName = Tk_GetUid(argv[3]);
  1216.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1217.         XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName);
  1218.         }
  1219.     }
  1220.     } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
  1221.         && (length >= 5)) {
  1222.     int x, y;
  1223.  
  1224.     if ((argc != 3) && (argc != 5)) {
  1225.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1226.             argv[0], " iconposition window ?x y?\"",
  1227.             (char *) NULL);
  1228.         return TCL_ERROR;
  1229.     }
  1230.     if (argc == 3) {
  1231.         if (wmPtr->hints.flags & IconPositionHint) {
  1232.         sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
  1233.             wmPtr->hints.icon_y);
  1234.         }
  1235.         return TCL_OK;
  1236.     }
  1237.     if (*argv[3] == '\0') {
  1238.         wmPtr->hints.flags &= ~IconPositionHint;
  1239.     } else {
  1240.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
  1241.             || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
  1242.         return TCL_ERROR;
  1243.         }
  1244.         wmPtr->hints.icon_x = x;
  1245.         wmPtr->hints.icon_y = y;
  1246.         wmPtr->hints.flags |= IconPositionHint;
  1247.     }
  1248.     UpdateHints(winPtr);
  1249.     } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
  1250.         && (length >= 5)) {
  1251.     Tk_Window tkwin2;
  1252.     WmInfo *wmPtr2;
  1253.     XSetWindowAttributes atts;
  1254.  
  1255.     if ((argc != 3) && (argc != 4)) {
  1256.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1257.             argv[0], " iconwindow window ?pathName?\"",
  1258.             (char *) NULL);
  1259.         return TCL_ERROR;
  1260.     }
  1261.     if (argc == 3) {
  1262.         if (wmPtr->icon != NULL) {
  1263.         interp->result = Tk_PathName(wmPtr->icon);
  1264.         }
  1265.         return TCL_OK;
  1266.     }
  1267.     if (*argv[3] == '\0') {
  1268.         wmPtr->hints.flags &= ~IconWindowHint;
  1269.         if (wmPtr->icon != NULL) {
  1270.         /*
  1271.          * Let the window use button events again, then remove
  1272.          * it as icon window.
  1273.          */
  1274.  
  1275.         atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
  1276.             | ButtonPressMask;
  1277.         Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
  1278.         wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
  1279.         wmPtr2->iconFor = NULL;
  1280.         wmPtr2->withdrawn = 1;
  1281.         wmPtr2->hints.initial_state = WithdrawnState;
  1282.         }
  1283.         wmPtr->icon = NULL;
  1284.     } else {
  1285.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  1286.         if (tkwin2 == NULL) {
  1287.         return TCL_ERROR;
  1288.         }
  1289.         if (!Tk_IsTopLevel(tkwin2)) {
  1290.         Tcl_AppendResult(interp, "can't use ", argv[3],
  1291.             " as icon window: not at top level", (char *) NULL);
  1292.         return TCL_ERROR;
  1293.         }
  1294.         wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
  1295.         if (wmPtr2->iconFor != NULL) {
  1296.         Tcl_AppendResult(interp, argv[3], " is already an icon for ",
  1297.             Tk_PathName(wmPtr2->iconFor), (char *) NULL);
  1298.         return TCL_ERROR;
  1299.         }
  1300.         if (wmPtr->icon != NULL) {
  1301.         WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
  1302.         wmPtr3->iconFor = NULL;
  1303.         wmPtr3->withdrawn = 1;
  1304.  
  1305.         /*
  1306.          * Let the window use button events again.
  1307.          */
  1308.  
  1309.         atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask
  1310.             | ButtonPressMask;
  1311.         Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts);
  1312.         }
  1313.  
  1314.         /*
  1315.          * Disable button events in the icon window:  some window
  1316.          * managers (like olvwm) want to get the events themselves,
  1317.          * but X only allows one application at a time to receive
  1318.          * button events for a window.
  1319.          */
  1320.  
  1321.         atts.event_mask = Tk_Attributes(tkwin2)->event_mask
  1322.             & ~ButtonPressMask;
  1323.         Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts);
  1324.         Tk_MakeWindowExist(tkwin2);
  1325.         wmPtr->hints.icon_window = Tk_WindowId(tkwin2);
  1326.         wmPtr->hints.flags |= IconWindowHint;
  1327.         wmPtr->icon = tkwin2;
  1328.         wmPtr2->iconFor = (Tk_Window) winPtr;
  1329.         if (!wmPtr2->withdrawn && !(wmPtr2->flags & WM_NEVER_MAPPED)) {
  1330.         wmPtr2->withdrawn = 0;
  1331.         if (XWithdrawWindow(Tk_Display(tkwin2), Tk_WindowId(tkwin2),
  1332.             Tk_ScreenNumber(tkwin2)) == 0) {
  1333.             interp->result =
  1334.                 "couldn't send withdraw message to window manager";
  1335.             return TCL_ERROR;
  1336.         }
  1337.         WaitForMapNotify((TkWindow *) tkwin2, 0);
  1338.         }
  1339.     }
  1340.     UpdateHints(winPtr);
  1341.     } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
  1342.         && (length >= 2)) {
  1343.     int width, height;
  1344.     if ((argc != 3) && (argc != 5)) {
  1345.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1346.             argv[0], " maxsize window ?width height?\"", (char *) NULL);
  1347.         return TCL_ERROR;
  1348.     }
  1349.     if (argc == 3) {
  1350.         sprintf(interp->result, "%d %d", wmPtr->maxWidth,
  1351.             wmPtr->maxHeight);
  1352.         return TCL_OK;
  1353.     }
  1354.     if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  1355.         || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  1356.         return TCL_ERROR;
  1357.     }
  1358.     wmPtr->maxWidth = width;
  1359.     wmPtr->maxHeight = height;
  1360.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1361.     goto updateGeom;
  1362.     } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
  1363.         && (length >= 2)) {
  1364.     int width, height;
  1365.     if ((argc != 3) && (argc != 5)) {
  1366.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1367.             argv[0], " minsize window ?width height?\"", (char *) NULL);
  1368.         return TCL_ERROR;
  1369.     }
  1370.     if (argc == 3) {
  1371.         sprintf(interp->result, "%d %d", wmPtr->minWidth,
  1372.             wmPtr->minHeight);
  1373.         return TCL_OK;
  1374.     }
  1375.     if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  1376.         || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  1377.         return TCL_ERROR;
  1378.     }
  1379.     wmPtr->minWidth = width;
  1380.     wmPtr->minHeight = height;
  1381.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1382.     goto updateGeom;
  1383.     } else if ((c == 'o')
  1384.         && (strncmp(argv[1], "overrideredirect", length) == 0)) {
  1385.     int boolean;
  1386.     XSetWindowAttributes atts;
  1387.  
  1388.     if ((argc != 3) && (argc != 4)) {
  1389.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1390.             argv[0], " overrideredirect window ?boolean?\"",
  1391.             (char *) NULL);
  1392.         return TCL_ERROR;
  1393.     }
  1394.     if (argc == 3) {
  1395.         if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
  1396.         interp->result = "1";
  1397.         } else {
  1398.         interp->result = "0";
  1399.         }
  1400.         return TCL_OK;
  1401.     }
  1402.     if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {
  1403.         return TCL_ERROR;
  1404.     }
  1405.     atts.override_redirect = (boolean) ? True : False;
  1406.     Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
  1407.         &atts);
  1408.     } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)
  1409.         && (length >= 2)) {
  1410.     if ((argc != 3) && (argc != 4)) {
  1411.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1412.             argv[0], " positionfrom window ?user/program?\"",
  1413.             (char *) NULL);
  1414.         return TCL_ERROR;
  1415.     }
  1416.     if (argc == 3) {
  1417.         if (wmPtr->sizeHintsFlags & USPosition) {
  1418.         interp->result = "user";
  1419.         } else if (wmPtr->sizeHintsFlags & PPosition) {
  1420.         interp->result = "program";
  1421.         }
  1422.         return TCL_OK;
  1423.     }
  1424.     if (*argv[3] == '\0') {
  1425.         wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
  1426.     } else {
  1427.         c = argv[3][0];
  1428.         length = strlen(argv[3]);
  1429.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  1430.         wmPtr->sizeHintsFlags &= ~PPosition;
  1431.         wmPtr->sizeHintsFlags |= USPosition;
  1432.         } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) {
  1433.         wmPtr->sizeHintsFlags &= ~USPosition;
  1434.         wmPtr->sizeHintsFlags |= PPosition;
  1435.         } else {
  1436.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  1437.             "\": must be program or user", (char *) NULL);
  1438.         return TCL_ERROR;
  1439.         }
  1440.     }
  1441.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1442.     goto updateGeom;
  1443.     } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)
  1444.         && (length >= 2)) {
  1445.     register ProtocolHandler *protPtr, *prevPtr;
  1446.     Atom protocol;
  1447.     int cmdLength;
  1448.  
  1449.     if ((argc < 3) || (argc > 5)) {
  1450.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1451.             argv[0], " protocol window ?name? ?command?\"",
  1452.             (char *) NULL);
  1453.         return TCL_ERROR;
  1454.     }
  1455.     if (argc == 3) {
  1456.         /*
  1457.          * Return a list of all defined protocols for the window.
  1458.          */
  1459.         for (protPtr = wmPtr->protPtr; protPtr != NULL;
  1460.             protPtr = protPtr->nextPtr) {
  1461.         Tcl_AppendElement(interp,
  1462.             Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
  1463.         }
  1464.         return TCL_OK;
  1465.     }
  1466.     protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
  1467.     if (argc == 4) {
  1468.         /*
  1469.          * Return the command to handle a given protocol.
  1470.          */
  1471.         for (protPtr = wmPtr->protPtr; protPtr != NULL;
  1472.             protPtr = protPtr->nextPtr) {
  1473.         if (protPtr->protocol == protocol) {
  1474.             interp->result = protPtr->command;
  1475.             return TCL_OK;
  1476.         }
  1477.         }
  1478.         return TCL_OK;
  1479.     }
  1480.  
  1481.     /*
  1482.      * Delete any current protocol handler, then create a new
  1483.      * one with the specified command, unless the command is
  1484.      * empty.
  1485.      */
  1486.  
  1487.     for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
  1488.         prevPtr = protPtr, protPtr = protPtr->nextPtr) {
  1489.         if (protPtr->protocol == protocol) {
  1490.         if (prevPtr == NULL) {
  1491.             wmPtr->protPtr = protPtr->nextPtr;
  1492.         } else {
  1493.             prevPtr->nextPtr = protPtr->nextPtr;
  1494.         }
  1495.         Tk_EventuallyFree((ClientData) protPtr, (Tk_FreeProc *) free);
  1496.         break;
  1497.         }
  1498.     }
  1499.     cmdLength = strlen(argv[4]);
  1500.     if (cmdLength > 0) {
  1501.         protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
  1502.         protPtr->protocol = protocol;
  1503.         protPtr->nextPtr = wmPtr->protPtr;
  1504.         wmPtr->protPtr = protPtr;
  1505.         protPtr->interp = interp;
  1506.         strcpy(protPtr->command, argv[4]);
  1507.     }
  1508.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1509.         UpdateWmProtocols(wmPtr);
  1510.     }
  1511.     } else if ((c == 'r') && (strncmp(argv[1], "resizable", length) == 0)) {
  1512.     int width, height;
  1513.  
  1514.     if ((argc != 3) && (argc != 5)) {
  1515.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1516.             argv[0], " resizable window ?width height?\"",
  1517.             (char *) NULL);
  1518.         return TCL_ERROR;
  1519.     }
  1520.     if (argc == 3) {
  1521.         sprintf(interp->result, "%d %d",
  1522.             (wmPtr->flags  & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
  1523.             (wmPtr->flags  & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
  1524.         return TCL_OK;
  1525.     }
  1526.     if ((Tcl_GetBoolean(interp, argv[3], &width) != TCL_OK)
  1527.         || (Tcl_GetBoolean(interp, argv[4], &height) != TCL_OK)) {
  1528.         return TCL_ERROR;
  1529.     }
  1530.     if (width) {
  1531.         wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;
  1532.     } else {
  1533.         wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
  1534.     }
  1535.     if (height) {
  1536.         wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;
  1537.     } else {
  1538.         wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
  1539.     }
  1540.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1541.     goto updateGeom;
  1542.     } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)
  1543.         && (length >= 2)) {
  1544.     if ((argc != 3) && (argc != 4)) {
  1545.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1546.             argv[0], " sizefrom window ?user|program?\"",
  1547.             (char *) NULL);
  1548.         return TCL_ERROR;
  1549.     }
  1550.     if (argc == 3) {
  1551.         if (wmPtr->sizeHintsFlags & USSize) {
  1552.         interp->result = "user";
  1553.         } else if (wmPtr->sizeHintsFlags & PSize) {
  1554.         interp->result = "program";
  1555.         }
  1556.         return TCL_OK;
  1557.     }
  1558.     if (*argv[3] == '\0') {
  1559.         wmPtr->sizeHintsFlags &= ~(USSize|PSize);
  1560.     } else {
  1561.         c = argv[3][0];
  1562.         length = strlen(argv[3]);
  1563.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  1564.         wmPtr->sizeHintsFlags &= ~PSize;
  1565.         wmPtr->sizeHintsFlags |= USSize;
  1566.         } else if ((c == 'p')
  1567.             && (strncmp(argv[3], "program", length) == 0)) {
  1568.         wmPtr->sizeHintsFlags &= ~USSize;
  1569.         wmPtr->sizeHintsFlags |= PSize;
  1570.         } else {
  1571.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  1572.             "\": must be program or user", (char *) NULL);
  1573.         return TCL_ERROR;
  1574.         }
  1575.     }
  1576.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1577.     goto updateGeom;
  1578.     } else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)
  1579.         && (length >= 2)) {
  1580.     if (argc != 3) {
  1581.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1582.             argv[0], " state window\"", (char *) NULL);
  1583.         return TCL_ERROR;
  1584.     }
  1585.     if (wmPtr->iconFor != NULL) {
  1586.         interp->result = "icon";
  1587.     } else if (wmPtr->withdrawn) {
  1588.         interp->result = "withdrawn";
  1589.     } else if (Tk_IsMapped((Tk_Window) winPtr)
  1590.         || ((wmPtr->flags & WM_NEVER_MAPPED)
  1591.         && (wmPtr->hints.initial_state == NormalState))) {
  1592.         interp->result = "normal";
  1593.     } else {
  1594.         interp->result = "iconic";
  1595.     }
  1596.     } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
  1597.         && (length >= 2)) {
  1598.     if (argc > 4) {
  1599.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1600.             argv[0], " title window ?newTitle?\"", (char *) NULL);
  1601.         return TCL_ERROR;
  1602.     }
  1603.     if (argc == 3) {
  1604.         interp->result = (wmPtr->titleUid != NULL) ? wmPtr->titleUid
  1605.             : winPtr->nameUid;
  1606.         return TCL_OK;
  1607.     } else {
  1608.         wmPtr->titleUid = Tk_GetUid(argv[3]);
  1609.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1610.         XTextProperty textProp;
  1611.  
  1612.         if (XStringListToTextProperty(&wmPtr->titleUid, 1,
  1613.             &textProp)  != 0) {
  1614.             XSetWMName(winPtr->display, winPtr->window, &textProp);
  1615.             XFree((char *) textProp.value);
  1616.         }
  1617.         }
  1618.     }
  1619.     } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
  1620.         && (length >= 3)) {
  1621.     Tk_Window master;
  1622.  
  1623.     if ((argc != 3) && (argc != 4)) {
  1624.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1625.             argv[0], " transient window ?master?\"", (char *) NULL);
  1626.         return TCL_ERROR;
  1627.     }
  1628.     if (argc == 3) {
  1629.         if (wmPtr->master != None) {
  1630.         interp->result = wmPtr->masterWindowName;
  1631.         }
  1632.         return TCL_OK;
  1633.     }
  1634.     if (argv[3][0] == '\0') {
  1635.         wmPtr->master = None;
  1636.         wmPtr->masterWindowName = NULL;
  1637.     } else {
  1638.         master = Tk_NameToWindow(interp, argv[3], tkwin);
  1639.         if (master == NULL) {
  1640.         return TCL_ERROR;
  1641.         }
  1642.         Tk_MakeWindowExist(master);
  1643.         wmPtr->master = Tk_WindowId(master);
  1644.         wmPtr->masterWindowName = Tk_PathName(master);
  1645.     }
  1646.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1647.         XSetTransientForHint(winPtr->display, winPtr->window,
  1648.             wmPtr->master);
  1649.     }
  1650.     } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)) {
  1651.     if (argc != 3) {
  1652.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1653.             argv[0], " withdraw window\"", (char *) NULL);
  1654.         return TCL_ERROR;
  1655.     }
  1656.     if (wmPtr->iconFor != NULL) {
  1657.         Tcl_AppendResult(interp, "can't withdraw ", argv[2],
  1658.             ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
  1659.             (char *) NULL);
  1660.         return TCL_ERROR;
  1661.     }
  1662.     wmPtr->hints.initial_state = WithdrawnState;
  1663.     wmPtr->withdrawn = 1;
  1664.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1665.         return TCL_OK;
  1666.     }
  1667.     if (XWithdrawWindow(winPtr->display, winPtr->window,
  1668.         winPtr->screenNum) == 0) {
  1669.         interp->result =
  1670.             "couldn't send withdraw message to window manager";
  1671.         return TCL_ERROR;
  1672.     }
  1673.     WaitForMapNotify(winPtr, 0);
  1674.     } else {
  1675.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  1676.         "\": must be aspect, client, command, deiconify, ",
  1677.         "focusmodel, frame, geometry, grid, group, iconbitmap, ",
  1678.         "iconify, iconmask, iconname, iconposition, ",
  1679.         "iconwindow, maxsize, minsize, overrideredirect, ",
  1680.         "positionfrom, protocol, resizable, sizefrom, state, title, ",
  1681.         "tracing, transient, or withdraw",
  1682.         (char *) NULL);
  1683.     return TCL_ERROR;
  1684.     }
  1685.     return TCL_OK;
  1686.  
  1687.     updateGeom:
  1688.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1689.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1690.     wmPtr->flags |= WM_UPDATE_PENDING;
  1691.     }
  1692.     return TCL_OK;
  1693. }
  1694.  
  1695. /*
  1696.  *----------------------------------------------------------------------
  1697.  *
  1698.  * Tk_SetGrid --
  1699.  *
  1700.  *    This procedure is invoked by a widget when it wishes to set a grid
  1701.  *    coordinate system that controls the size of a top-level window.
  1702.  *    It provides a C interface equivalent to the "wm grid" command and
  1703.  *    is usually asscoiated with the -setgrid option.
  1704.  *
  1705.  * Results:
  1706.  *    None.
  1707.  *
  1708.  * Side effects:
  1709.  *    Grid-related information will be passed to the window manager, so
  1710.  *    that the top-level window associated with tkwin will resize on
  1711.  *    even grid units.  If some other window already controls gridding
  1712.  *    for the top-level window then this procedure call has no effect.
  1713.  *
  1714.  *----------------------------------------------------------------------
  1715.  */
  1716.  
  1717. void
  1718. Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
  1719.     Tk_Window tkwin;        /* Token for window.  New window mgr info
  1720.                  * will be posted for the top-level window
  1721.                  * associated with this window. */
  1722.     int reqWidth;        /* Width (in grid units) corresponding to
  1723.                  * the requested geometry for tkwin. */
  1724.     int reqHeight;        /* Height (in grid units) corresponding to
  1725.                  * the requested geometry for tkwin. */
  1726.     int widthInc, heightInc;    /* Pixel increments corresponding to a
  1727.                  * change of one grid unit. */
  1728. {
  1729.     TkWindow *winPtr = (TkWindow *) tkwin;
  1730.     register WmInfo *wmPtr;
  1731.  
  1732.     /*
  1733.      * Find the top-level window for tkwin, plus the window manager
  1734.      * information.
  1735.      */
  1736.  
  1737.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  1738.     winPtr = winPtr->parentPtr;
  1739.     if (winPtr == NULL) {
  1740.         /*
  1741.          * The window is being deleted... just skip this operation.
  1742.          */
  1743.  
  1744.         return;
  1745.     }
  1746.     }
  1747.     wmPtr = winPtr->wmInfoPtr;
  1748.  
  1749.     if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {
  1750.     return;
  1751.     }
  1752.  
  1753.     if ((wmPtr->reqGridWidth == reqWidth)
  1754.         && (wmPtr->reqGridHeight == reqHeight)
  1755.         && (wmPtr->widthInc == widthInc)
  1756.         && (wmPtr->heightInc == heightInc)
  1757.         && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
  1758.             == PBaseSize|PResizeInc)) {
  1759.     return;
  1760.     }
  1761.  
  1762.     /*
  1763.      * If gridding was previously off, then forget about any window
  1764.      * size requests made by the user or via "wm geometry":  these are
  1765.      * in pixel units and there's no easy way to translate them to
  1766.      * grid units since the new requested size of the top-level window in
  1767.      * pixels may not yet have been registered yet (it may filter up
  1768.      * the hierarchy in DoWhenIdle handlers).
  1769.      */
  1770.  
  1771.     if (wmPtr->gridWin == NULL) {
  1772.     wmPtr->width = -1;
  1773.     wmPtr->height = -1;
  1774.     }
  1775.  
  1776.     /* 
  1777.      * Set the new gridding information, and start the process of passing
  1778.      * all of this information to the window manager.
  1779.      */
  1780.  
  1781.     wmPtr->gridWin = tkwin;
  1782.     wmPtr->reqGridWidth = reqWidth;
  1783.     wmPtr->reqGridHeight = reqHeight;
  1784.     wmPtr->widthInc = widthInc;
  1785.     wmPtr->heightInc = heightInc;
  1786.     wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
  1787.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1788.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1789.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1790.     wmPtr->flags |= WM_UPDATE_PENDING;
  1791.     }
  1792. }
  1793.  
  1794. /*
  1795.  *----------------------------------------------------------------------
  1796.  *
  1797.  * Tk_UnsetGrid --
  1798.  *
  1799.  *    This procedure cancels the effect of a previous call
  1800.  *    to Tk_SetGrid.
  1801.  *
  1802.  * Results:
  1803.  *    None.
  1804.  *
  1805.  * Side effects:
  1806.  *    If tkwin currently controls gridding for its top-level window,
  1807.  *    gridding is cancelled for that top-level window;  if some other
  1808.  *    window controls gridding then this procedure has no effect.
  1809.  *
  1810.  *----------------------------------------------------------------------
  1811.  */
  1812.  
  1813. void
  1814. Tk_UnsetGrid(tkwin)
  1815.     Tk_Window tkwin;        /* Token for window that is currently
  1816.                  * controlling gridding. */
  1817. {
  1818.     TkWindow *winPtr = (TkWindow *) tkwin;
  1819.     register WmInfo *wmPtr;
  1820.  
  1821.     /*
  1822.      * Find the top-level window for tkwin, plus the window manager
  1823.      * information.
  1824.      */
  1825.  
  1826.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  1827.     winPtr = winPtr->parentPtr;
  1828.     if (winPtr == NULL) {
  1829.         /*
  1830.          * The window is being deleted... just skip this operation.
  1831.          */
  1832.  
  1833.         return;
  1834.     }
  1835.     }
  1836.     wmPtr = winPtr->wmInfoPtr;
  1837.     if (tkwin != wmPtr->gridWin) {
  1838.     return;
  1839.     }
  1840.  
  1841.     wmPtr->gridWin = NULL;
  1842.     wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
  1843.     if (wmPtr->width != -1) {
  1844.     wmPtr->width = winPtr->reqWidth + (wmPtr->width
  1845.         - wmPtr->reqGridWidth)*wmPtr->widthInc;
  1846.     wmPtr->height = winPtr->reqHeight + (wmPtr->height
  1847.         - wmPtr->reqGridHeight)*wmPtr->heightInc;
  1848.     }
  1849.     wmPtr->widthInc = 1;
  1850.     wmPtr->heightInc = 1;
  1851.  
  1852.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1853.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1854.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1855.     wmPtr->flags |= WM_UPDATE_PENDING;
  1856.     }
  1857. }
  1858.  
  1859. /*
  1860.  *----------------------------------------------------------------------
  1861.  *
  1862.  * ConfigureEvent --
  1863.  *
  1864.  *    This procedure is called to handle ConfigureNotify events on
  1865.  *    top-level windows.
  1866.  *
  1867.  * Results:
  1868.  *    None.
  1869.  *
  1870.  * Side effects:
  1871.  *    Information gets updated in the WmInfo structure for the window.
  1872.  *
  1873.  *----------------------------------------------------------------------
  1874.  */
  1875.  
  1876. static void
  1877. ConfigureEvent(winPtr, configEventPtr)
  1878.     TkWindow *winPtr;            /* Top-level window. */
  1879.     XConfigureEvent *configEventPtr;    /* Event that just occurred for
  1880.                      * winPtr. */
  1881. {
  1882.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1883.  
  1884.     /* 
  1885.      * Update size information from the event.  There are a couple of
  1886.      * tricky points here:
  1887.      *
  1888.      * 1. If the user changed the size externally then set wmPtr->width
  1889.      *    and wmPtr->height just as if a "wm geometry" command had been
  1890.      *    invoked with the same information.
  1891.      * 2. However, if the size is changing in response to a request
  1892.      *    coming from us (WM_SYNC_PENDING is set), then don't set wmPtr->width
  1893.      *    or wmPtr->height if they were previously -1 (otherwise the
  1894.      *    window will stop tracking geometry manager requests).
  1895.      */
  1896.  
  1897.     if (((winPtr->changes.width != configEventPtr->width)
  1898.         || (winPtr->changes.height != configEventPtr->height))
  1899.         && !(wmPtr->flags & WM_SYNC_PENDING)){
  1900.     if (wmTracing) {
  1901.         printf("TopLevelEventProc: user changed %s size to %dx%d\n",
  1902.             winPtr->pathName, configEventPtr->width,
  1903.             configEventPtr->height);
  1904.     }
  1905.     if (wmPtr->gridWin != NULL) {
  1906.         wmPtr->width = wmPtr->reqGridWidth
  1907.             + (configEventPtr->width
  1908.             - winPtr->reqWidth)/wmPtr->widthInc;
  1909.         if (wmPtr->width < 0) {
  1910.         wmPtr->width = 0;
  1911.         }
  1912.         wmPtr->height = wmPtr->reqGridHeight
  1913.             + (configEventPtr->height
  1914.             - winPtr->reqHeight)/wmPtr->heightInc;
  1915.         if (wmPtr->height < 0) {
  1916.         wmPtr->height = 0;
  1917.         }
  1918.     } else {
  1919.         wmPtr->width = configEventPtr->width;
  1920.         wmPtr->height = configEventPtr->height;
  1921.     }
  1922.     wmPtr->configWidth = configEventPtr->width;
  1923.     wmPtr->configHeight = configEventPtr->height;
  1924.     }
  1925.  
  1926.     if (wmTracing) {
  1927.     printf("ConfigureEvent: %s x = %d y = %d, width = %d, height = %d",
  1928.         winPtr->pathName, configEventPtr->x, configEventPtr->y,
  1929.         configEventPtr->width, configEventPtr->height);
  1930.     printf(" send_event = %d, serial = %ld\n", configEventPtr->send_event,
  1931.         configEventPtr->serial);
  1932.     }
  1933.     winPtr->changes.width = configEventPtr->width;
  1934.     winPtr->changes.height = configEventPtr->height;
  1935.     winPtr->changes.border_width = configEventPtr->border_width;
  1936.     winPtr->changes.sibling = configEventPtr->above;
  1937.     winPtr->changes.stack_mode = Above;
  1938.  
  1939.     /*
  1940.      * Reparenting window managers make life difficult.  If the
  1941.      * window manager reparents a top-level window then the x and y
  1942.      * information that comes in events for the window is wrong:
  1943.      * it gives the location of the window inside its decorative
  1944.      * parent, rather than the location of the window in root
  1945.      * coordinates, which is what we want.  Window managers
  1946.      * are supposed to send synthetic events with the correct
  1947.      * information, but ICCCM doesn't require them to do this
  1948.      * under all conditions, and the information provided doesn't
  1949.      * include everything we need here.  So, the code below
  1950.      * maintains a bunch of information about the parent window.
  1951.      * If the window hasn't been reparented, we pretend that
  1952.      * there is a parent shrink-wrapped around the window.
  1953.      */
  1954.  
  1955.     if ((wmPtr->reparent == None) || !ComputeReparentGeometry(winPtr)) {
  1956.     wmPtr->parentWidth = configEventPtr->width
  1957.         + 2*configEventPtr->border_width;
  1958.     wmPtr->parentHeight = configEventPtr->height
  1959.         + 2*configEventPtr->border_width;
  1960.     winPtr->changes.x = wmPtr->x = configEventPtr->x;
  1961.     winPtr->changes.y = wmPtr->y = configEventPtr->y;
  1962.     if (wmPtr->flags & WM_NEGATIVE_X) {
  1963.         wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
  1964.     }
  1965.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  1966.         wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
  1967.     }
  1968.     }
  1969. }
  1970.  
  1971. /*
  1972.  *----------------------------------------------------------------------
  1973.  *
  1974.  * ReparentEvent --
  1975.  *
  1976.  *    This procedure is called to handle ReparentNotify events on
  1977.  *    top-level windows.
  1978.  *
  1979.  * Results:
  1980.  *    None.
  1981.  *
  1982.  * Side effects:
  1983.  *    Information gets updated in the WmInfo structure for the window.
  1984.  *
  1985.  *----------------------------------------------------------------------
  1986.  */
  1987.  
  1988. static void
  1989. ReparentEvent(winPtr, reparentEventPtr)
  1990.     TkWindow *winPtr;            /* Top-level window. */
  1991.     XReparentEvent *reparentEventPtr;    /* Event that just occurred for
  1992.                      * winPtr. */
  1993. {
  1994.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  1995.     Window vRoot, ancestor, *children, dummy2, *virtualRootPtr;
  1996.     Atom actualType;
  1997.     int actualFormat;
  1998.     unsigned long numItems, bytesAfter;
  1999.     unsigned int dummy;
  2000.     Tk_ErrorHandler handler;
  2001.  
  2002.     /*
  2003.      * Identify the root window for winPtr.  This is tricky because of
  2004.      * virtual root window managers like tvtwm.  If the window has a
  2005.      * property named __SWM_ROOT or __WM_ROOT then this property gives
  2006.      * the id for a virtual root window that should be used instead of
  2007.      * the root window of the screen.
  2008.      */
  2009.  
  2010.     vRoot = RootWindow(winPtr->display, winPtr->screenNum);
  2011.     wmPtr->vRoot = None;
  2012.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  2013.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2014.     if (((XGetWindowProperty(winPtr->display, winPtr->window,
  2015.         Tk_InternAtom((Tk_Window) winPtr, "__WM_ROOT"), 0, (long) 1,
  2016.         False, XA_WINDOW, &actualType, &actualFormat, &numItems,
  2017.         &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
  2018.         && (actualType == XA_WINDOW))
  2019.         || ((XGetWindowProperty(winPtr->display, winPtr->window,
  2020.         Tk_InternAtom((Tk_Window) winPtr, "__SWM_ROOT"), 0, (long) 1,
  2021.         False, XA_WINDOW, &actualType, &actualFormat, &numItems,
  2022.         &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
  2023.         && (actualType == XA_WINDOW))) {
  2024.     if ((actualFormat == 32) && (numItems == 1)) {
  2025.         vRoot = wmPtr->vRoot = *virtualRootPtr;
  2026.     } else if (wmTracing) {
  2027.         printf("%s format %d numItems %ld\n",
  2028.             "ReparentEvent got bogus VROOT property:", actualFormat,
  2029.             numItems);
  2030.     }
  2031.     XFree((char *) virtualRootPtr);
  2032.     }
  2033.     Tk_DeleteErrorHandler(handler);
  2034.  
  2035.     if (wmTracing) {
  2036.     printf("ReparentEvent: %s reparented to 0x%x, vRoot = 0x%x\n",
  2037.         winPtr->pathName, (unsigned int) reparentEventPtr->parent,
  2038.         (unsigned int) vRoot);
  2039.     }
  2040.  
  2041.     /*
  2042.      * Fetch correct geometry information for the new virtual root.
  2043.      */
  2044.  
  2045.     UpdateVRootGeometry(wmPtr);
  2046.  
  2047.     /*
  2048.      * If the window's new parent is the root window, then mark it as
  2049.      * no longer reparented.
  2050.      */
  2051.  
  2052.     if (reparentEventPtr->parent == vRoot) {
  2053.     noReparent:
  2054.     wmPtr->reparent = None;
  2055.     wmPtr->parentWidth = winPtr->changes.width
  2056.         + 2*winPtr->changes.border_width;
  2057.     wmPtr->parentHeight = winPtr->changes.height
  2058.         + 2*winPtr->changes.border_width;
  2059.     wmPtr->xInParent = wmPtr->yInParent = 0;
  2060.     winPtr->changes.x = reparentEventPtr->x;
  2061.     winPtr->changes.y = reparentEventPtr->y;
  2062.     return;
  2063.     }
  2064.  
  2065.     /*
  2066.      * Search up the window hierarchy to find the ancestor of this
  2067.      * window that is just below the (virtual) root.  This is tricky
  2068.      * because it's possible that things have changed since the event
  2069.      * was generated so that the ancestry indicated by the event no
  2070.      * longer exists.  If this happens then an error will occur and
  2071.      * we just discard the event (there will be a more up-to-date
  2072.      * ReparentNotify event coming later).
  2073.      */
  2074.  
  2075.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  2076.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2077.     wmPtr->reparent = reparentEventPtr->parent;
  2078.     while (1) {
  2079.     if (XQueryTree(winPtr->display, wmPtr->reparent, &dummy2, &ancestor,
  2080.         &children, &dummy) == 0) {
  2081.         Tk_DeleteErrorHandler(handler);
  2082.         goto noReparent;
  2083.     }
  2084.     XFree((char *) children);
  2085.     if ((ancestor == vRoot) ||
  2086.         (ancestor == RootWindow(winPtr->display, winPtr->screenNum))) {
  2087.         break;
  2088.     }
  2089.     wmPtr->reparent = ancestor;
  2090.     }
  2091.     Tk_DeleteErrorHandler(handler);
  2092.  
  2093.     if (!ComputeReparentGeometry(winPtr)) {
  2094.     goto noReparent;
  2095.     }
  2096. }
  2097.  
  2098. /*
  2099.  *----------------------------------------------------------------------
  2100.  *
  2101.  * ComputeReparentGeometry --
  2102.  *
  2103.  *    This procedure is invoked to recompute geometry information
  2104.  *    related to a reparented top-level window, such as the position
  2105.  *    and total size of the parent and the position within it of
  2106.  *    the top-level window.
  2107.  *
  2108.  * Results:
  2109.  *    The return value is 1 if everything completed successfully
  2110.  *    and 0 if an error occurred while querying information about
  2111.  *    winPtr's parents.  In this case winPtr is marked as no longer
  2112.  *    being reparented.
  2113.  *
  2114.  * Side effects:
  2115.  *    Geometry information in winPtr and winPtr->wmPtr gets updated.
  2116.  *
  2117.  *----------------------------------------------------------------------
  2118.  */
  2119.  
  2120. static int
  2121. ComputeReparentGeometry(winPtr)
  2122.     TkWindow *winPtr;        /* Top-level window whose reparent info
  2123.                  * is to be recomputed. */
  2124. {
  2125.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2126.     int width, height, bd;
  2127.     unsigned int dummy;
  2128.     int xOffset, yOffset, x, y;
  2129.     Window dummy2;
  2130.     Status status;
  2131.     Tk_ErrorHandler handler;
  2132.  
  2133.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  2134.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2135.     (void) XTranslateCoordinates(winPtr->display, winPtr->window,
  2136.         wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
  2137.     status = XGetGeometry(winPtr->display, wmPtr->reparent,
  2138.         &dummy2, &x, &y, (unsigned int *) &width,
  2139.         (unsigned int *) &height, (unsigned int *) &bd, &dummy);
  2140.     Tk_DeleteErrorHandler(handler);
  2141.     if (status == 0) {
  2142.     /*
  2143.      * It appears that the reparented parent went away and
  2144.      * no-one told us.  Reset the window to indicate that
  2145.      * it's not reparented.
  2146.      */
  2147.     wmPtr->reparent = None;
  2148.     wmPtr->xInParent = wmPtr->yInParent = 0;
  2149.     return 0;
  2150.     }
  2151.     wmPtr->xInParent = xOffset + bd - winPtr->changes.border_width;
  2152.     wmPtr->yInParent = yOffset + bd - winPtr->changes.border_width;
  2153.     wmPtr->parentWidth = width + 2*bd;
  2154.     wmPtr->parentHeight = height + 2*bd;
  2155.  
  2156.     /*
  2157.      * Some tricky issues in updating wmPtr->x and wmPtr->y:
  2158.      *
  2159.      * 1. Don't update them if the event occurred because of something
  2160.      * we did (i.e. WM_SYNC_PENDING and WM_MOVE_PENDING are both set).
  2161.      * This is because window managers treat coords differently than Tk,
  2162.      * and no two window managers are alike. If the window manager moved
  2163.      * the window because we told it to, remember the coordinates we told
  2164.      * it, not the ones it actually moved it to.  This allows us to move
  2165.      * the window back to the same coordinates later and get the same
  2166.      * result. Without this check, windows can "walk" across the screen
  2167.      * under some conditions.
  2168.      *
  2169.      * 2. Don't update wmPtr->x and wmPtr->y unless winPtr->changes.x
  2170.      * or winPtr->changes.y has changed (otherwise a size change can
  2171.      * spoof us into thinking that the position changed too and defeat
  2172.      * the intent of (1) above.
  2173.      *
  2174.      * 3. Ignore size changes coming from the window system if we're
  2175.      * about to change the size ourselves but haven't seen the event for
  2176.      * it yet:  our size change is supposed to take priority.
  2177.      */
  2178.  
  2179.     if (!(wmPtr->flags & WM_MOVE_PENDING)
  2180.         && ((winPtr->changes.x != (x + wmPtr->xInParent))
  2181.         || (winPtr->changes.y != (y + wmPtr->yInParent)))) {
  2182.     wmPtr->x = x;
  2183.     if (wmPtr->flags & WM_NEGATIVE_X) {
  2184.         wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
  2185.     }
  2186.     wmPtr->y = y;
  2187.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  2188.         wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
  2189.     }
  2190.     }
  2191.  
  2192.     winPtr->changes.x = x + wmPtr->xInParent;
  2193.     winPtr->changes.y = y + wmPtr->yInParent;
  2194.     if (wmTracing) {
  2195.     printf("winPtr coords %d,%d, wmPtr coords %d,%d, offsets %d %d\n",
  2196.         winPtr->changes.x, winPtr->changes.y, wmPtr->x, wmPtr->y,
  2197.         wmPtr->xInParent, wmPtr->yInParent);
  2198.     }
  2199.     return 1;
  2200. }
  2201.  
  2202. /*
  2203.  *----------------------------------------------------------------------
  2204.  *
  2205.  * TopLevelEventProc --
  2206.  *
  2207.  *    This procedure is invoked when a top-level (or other externally-
  2208.  *    managed window) is restructured in any way.
  2209.  *
  2210.  * Results:
  2211.  *    None.
  2212.  *
  2213.  * Side effects:
  2214.  *    Tk's internal data structures for the window get modified to
  2215.  *    reflect the structural change.
  2216.  *
  2217.  *----------------------------------------------------------------------
  2218.  */
  2219.  
  2220. static void
  2221. TopLevelEventProc(clientData, eventPtr)
  2222.     ClientData clientData;        /* Window for which event occurred. */
  2223.     XEvent *eventPtr;            /* Event that just happened. */
  2224. {
  2225.     register TkWindow *winPtr = (TkWindow *) clientData;
  2226.  
  2227.     winPtr->wmInfoPtr->flags |= WM_VROOT_OFFSET_STALE;
  2228.     if (eventPtr->type == DestroyNotify) {
  2229.     Tk_ErrorHandler handler;
  2230.  
  2231.     if (!(winPtr->flags & TK_ALREADY_DEAD)) {
  2232.         /*
  2233.          * A top-level window was deleted externally (e.g., by the window
  2234.          * manager).  This is probably not a good thing, but cleanup as
  2235.          * best we can.  The error handler is needed because
  2236.          * Tk_DestroyWindow will try to destroy the window, but of course
  2237.          * it's already gone.
  2238.          */
  2239.     
  2240.         handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  2241.             (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2242.         Tk_DestroyWindow((Tk_Window) winPtr);
  2243.         Tk_DeleteErrorHandler(handler);
  2244.     }
  2245.     if (wmTracing) {
  2246.         printf("TopLevelEventProc: %s deleted\n", winPtr->pathName);
  2247.     }
  2248.     } else if (eventPtr->type == ConfigureNotify) {
  2249.     /*
  2250.      * Ignore the event if the window has never been mapped yet.
  2251.      * Such an event occurs only in weird cases like changing the
  2252.      * internal border width of a top-level window, which results
  2253.      * in a synthetic Configure event.  These events are not relevant
  2254.      * to us, and if we process them confusion may result (e.g. we
  2255.      * may conclude erroneously that the user repositioned or resized
  2256.      * the window).
  2257.      */
  2258.  
  2259.     if (!(winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED)) {
  2260.         ConfigureEvent(winPtr, &eventPtr->xconfigure);
  2261.     }
  2262.     } else if (eventPtr->type == MapNotify) {
  2263.     winPtr->flags |= TK_MAPPED;
  2264.     if (wmTracing) {
  2265.         printf("TopLevelEventProc: %s mapped\n", winPtr->pathName);
  2266.     }
  2267.     } else if (eventPtr->type == UnmapNotify) {
  2268.     winPtr->flags &= ~TK_MAPPED;
  2269.     if (wmTracing) {
  2270.         printf("TopLevelEventProc: %s unmapped\n", winPtr->pathName);
  2271.     }
  2272.     } else if (eventPtr->type == ReparentNotify) {
  2273.     ReparentEvent(winPtr, &eventPtr->xreparent);
  2274.     }
  2275. }
  2276.  
  2277. /*
  2278.  *----------------------------------------------------------------------
  2279.  *
  2280.  * TopLevelReqProc --
  2281.  *
  2282.  *    This procedure is invoked by the geometry manager whenever
  2283.  *    the requested size for a top-level window is changed.
  2284.  *
  2285.  * Results:
  2286.  *    None.
  2287.  *
  2288.  * Side effects:
  2289.  *    Arrange for the window to be resized to satisfy the request
  2290.  *    (this happens as a when-idle action).
  2291.  *
  2292.  *----------------------------------------------------------------------
  2293.  */
  2294.  
  2295.     /* ARGSUSED */
  2296. static void
  2297. TopLevelReqProc(dummy, tkwin)
  2298.     ClientData dummy;            /* Not used. */
  2299.     Tk_Window tkwin;            /* Information about window. */
  2300. {
  2301.     TkWindow *winPtr = (TkWindow *) tkwin;
  2302.     WmInfo *wmPtr;
  2303.  
  2304.     wmPtr = winPtr->wmInfoPtr;
  2305.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  2306.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  2307.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  2308.     wmPtr->flags |= WM_UPDATE_PENDING;
  2309.     }
  2310.  
  2311.     /*
  2312.      * If the window isn't being positioned by its upper left corner
  2313.      * then we have to move it as well.
  2314.      */
  2315.  
  2316.     if (wmPtr->flags & (WM_NEGATIVE_X | WM_NEGATIVE_Y)) {
  2317.     wmPtr->flags |= WM_MOVE_PENDING;
  2318.     }
  2319. }
  2320.  
  2321. /*
  2322.  *----------------------------------------------------------------------
  2323.  *
  2324.  * UpdateGeometryInfo --
  2325.  *
  2326.  *    This procedure is invoked when a top-level window is first
  2327.  *    mapped, and also as a when-idle procedure, to bring the
  2328.  *    geometry and/or position of a top-level window back into
  2329.  *    line with what has been requested by the user and/or widgets.
  2330.  *    This procedure doesn't return until the window manager has
  2331.  *    responded to the geometry change.
  2332.  *
  2333.  * Results:
  2334.  *    None.
  2335.  *
  2336.  * Side effects:
  2337.  *    The window's size and location may change, unless the WM prevents
  2338.  *    that from happening.
  2339.  *
  2340.  *----------------------------------------------------------------------
  2341.  */
  2342.  
  2343. static void
  2344. UpdateGeometryInfo(clientData)
  2345.     ClientData clientData;        /* Pointer to the window's record. */
  2346. {
  2347.     register TkWindow *winPtr = (TkWindow *) clientData;
  2348.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2349.     int x, y, width, height;
  2350.     unsigned long serial;
  2351.  
  2352.     wmPtr->flags &= ~WM_UPDATE_PENDING;
  2353.  
  2354.     /*
  2355.      * Compute the new size for the top-level window.  See the
  2356.      * user documentation for details on this, but the size
  2357.      * requested depends on (a) the size requested internally
  2358.      * by the window's widgets, (b) the size requested by the
  2359.      * user in a "wm geometry" command or via wm-based interactive
  2360.      * resizing (if any), and (c) whether or not the window is
  2361.      * gridded.  Don't permit sizes <= 0 because this upsets
  2362.      * the X server.
  2363.      */
  2364.  
  2365.     if (wmPtr->width == -1) {
  2366.     width = winPtr->reqWidth;
  2367.     height = winPtr->reqHeight;
  2368.     } else if (wmPtr->gridWin != NULL) {
  2369.     width = winPtr->reqWidth
  2370.         + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
  2371.     height = winPtr->reqHeight
  2372.         + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
  2373.     } else {
  2374.     width = wmPtr->width;
  2375.     height = wmPtr->height;
  2376.     }
  2377.     if (width <= 0) {
  2378.     width = 1;
  2379.     }
  2380.     if (height <= 0) {
  2381.     height = 1;
  2382.     }
  2383.  
  2384.     /*
  2385.      * Compute the new position for the upper-left pixel of the window's
  2386.      * decorative frame.  This is tricky, because we need to include the
  2387.      * border widths supplied by a reparented parent in this calculation,
  2388.      * but can't use the parent's current overall size since that may
  2389.      * change as a result of this code.
  2390.      */
  2391.  
  2392.     if (wmPtr->flags & WM_NEGATIVE_X) {
  2393.     x = wmPtr->vRootWidth - wmPtr->x
  2394.         - (width + (wmPtr->parentWidth - winPtr->changes.width));
  2395.     } else {
  2396.     x =  wmPtr->x;
  2397.     }
  2398.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  2399.     y = wmPtr->vRootHeight - wmPtr->y
  2400.         - (height + (wmPtr->parentHeight - winPtr->changes.height));
  2401.     } else {
  2402.     y =  wmPtr->y;
  2403.     }
  2404.  
  2405.     /*
  2406.      * If the window's size is going to change and the window is
  2407.      * supposed to not be resizable by the user, then we have to
  2408.      * update the size hints.  There may also be a size-hint-update
  2409.      * request pending from somewhere else, too.
  2410.      */
  2411.  
  2412.     if (((width != winPtr->changes.width)
  2413.         || (height != winPtr->changes.height))
  2414.         && (wmPtr->gridWin == NULL)
  2415.         && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
  2416.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  2417.     }
  2418.     if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
  2419.     UpdateSizeHints(winPtr);
  2420.     }
  2421.  
  2422.     /*
  2423.      * Reconfigure the window if it isn't already configured correctly.
  2424.      * A few tricky points:
  2425.      *
  2426.      * 1. Sometimes the window manager will give us a different size
  2427.      *    than we asked for (e.g. mwm has a minimum size for windows), so
  2428.      *    base the size check on what we *asked for* last time, not what we
  2429.      *    got.
  2430.      * 2. Can't just reconfigure always, because we may not get a
  2431.      *    ConfigureNotify event back if nothing changed, so
  2432.      *    WaitForConfigureNotify will hang a long time.
  2433.      * 3. Don't move window unless a new position has been requested for
  2434.      *      it.  This is because of "features" in some window managers (e.g.
  2435.      *    twm, as of 4/24/91) where they don't interpret coordinates
  2436.      *    according to ICCCM.  Moving a window to its current location may
  2437.      *    cause it to shift position on the screen.
  2438.      */
  2439.  
  2440.     serial = NextRequest(winPtr->display);
  2441.     if (wmPtr->flags & WM_MOVE_PENDING) {
  2442.     wmPtr->configWidth = width;
  2443.     wmPtr->configHeight = height;
  2444.     if (wmTracing) {
  2445.         printf("UpdateGeometryInfo moving to %d %d, resizing to %d x %d,\n",
  2446.             x, y, width, height);
  2447.     }
  2448.     Tk_MoveResizeWindow((Tk_Window) winPtr, x, y, width, height);
  2449.     } else if ((width != wmPtr->configWidth)
  2450.         || (height != wmPtr->configHeight)) {
  2451.     wmPtr->configWidth = width;
  2452.     wmPtr->configHeight = height;
  2453.     if (wmTracing) {
  2454.         printf("UpdateGeometryInfo resizing to %d x %d\n", width, height);
  2455.     }
  2456.     Tk_ResizeWindow((Tk_Window) winPtr, width, height);
  2457.     } else {
  2458.     return;
  2459.     }
  2460.  
  2461.     /*
  2462.      * Wait for the configure operation to complete.  Don't need to do
  2463.      * this, however, if the window is about to be mapped:  it will be
  2464.      * taken care of elsewhere.
  2465.      */
  2466.  
  2467.     if (!(wmPtr->flags & WM_ABOUT_TO_MAP)) {
  2468.     WaitForConfigureNotify(winPtr, serial);
  2469.     }
  2470. }
  2471.  
  2472. /*
  2473.  *--------------------------------------------------------------
  2474.  *
  2475.  * UpdateSizeHints --
  2476.  *
  2477.  *    This procedure is called to update the window manager's
  2478.  *    size hints information from the information in a WmInfo
  2479.  *    structure.
  2480.  *
  2481.  * Results:
  2482.  *    None.
  2483.  *
  2484.  * Side effects:
  2485.  *    Properties get changed for winPtr.
  2486.  *
  2487.  *--------------------------------------------------------------
  2488.  */
  2489.  
  2490. static void
  2491. UpdateSizeHints(winPtr)
  2492.     TkWindow *winPtr;
  2493. {
  2494.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2495.     XSizeHints *hintsPtr;
  2496.  
  2497.     wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
  2498.  
  2499.     hintsPtr = XAllocSizeHints();
  2500.     if (hintsPtr == NULL) {
  2501.     return;
  2502.     }
  2503.  
  2504.     /*
  2505.      * Compute the pixel-based sizes for the various fields in the
  2506.      * size hints structure, based on the grid-based sizes in
  2507.      * our structure.
  2508.      */
  2509.  
  2510.     if (wmPtr->gridWin != NULL) {
  2511.     hintsPtr->base_width = winPtr->reqWidth
  2512.         - (wmPtr->reqGridWidth * wmPtr->widthInc);
  2513.     if (hintsPtr->base_width < 0) {
  2514.         hintsPtr->base_width = 0;
  2515.     }
  2516.     hintsPtr->base_height = winPtr->reqHeight
  2517.         - (wmPtr->reqGridHeight * wmPtr->heightInc);
  2518.     if (hintsPtr->base_height < 0) {
  2519.         hintsPtr->base_height = 0;
  2520.     }
  2521.     hintsPtr->min_width = hintsPtr->base_width
  2522.         + (wmPtr->minWidth * wmPtr->widthInc);
  2523.     hintsPtr->min_height = hintsPtr->base_height
  2524.         + (wmPtr->minHeight * wmPtr->heightInc);
  2525.     hintsPtr->max_width = hintsPtr->base_width
  2526.         + (wmPtr->maxWidth * wmPtr->widthInc);
  2527.     hintsPtr->max_height = hintsPtr->base_height
  2528.         + (wmPtr->maxHeight * wmPtr->heightInc);
  2529.     } else {
  2530.     hintsPtr->min_width = wmPtr->minWidth;
  2531.     hintsPtr->min_height = wmPtr->minHeight;
  2532.     hintsPtr->max_width = wmPtr->maxWidth;
  2533.     hintsPtr->max_height = wmPtr->maxHeight;
  2534.     hintsPtr->base_width = 0;
  2535.     hintsPtr->base_height = 0;
  2536.     }
  2537.     hintsPtr->width_inc = wmPtr->widthInc;
  2538.     hintsPtr->height_inc = wmPtr->heightInc;
  2539.     hintsPtr->min_aspect.x = wmPtr->minAspect.x;
  2540.     hintsPtr->min_aspect.y = wmPtr->minAspect.y;
  2541.     hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
  2542.     hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
  2543.     hintsPtr->win_gravity = wmPtr->gravity;
  2544.     hintsPtr->flags = wmPtr->sizeHintsFlags | PMinSize | PMaxSize;
  2545.  
  2546.     /*
  2547.      * If the window isn't supposed to be resizable, then set the
  2548.      * minimum and maximum dimensions to be the same.
  2549.      */
  2550.  
  2551.     if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
  2552.     if (wmPtr->width >= 0) {
  2553.         hintsPtr->min_width = wmPtr->width;
  2554.     } else {
  2555.         hintsPtr->min_width = winPtr->reqWidth;
  2556.     }
  2557.     hintsPtr->max_width = hintsPtr->min_width;
  2558.     }
  2559.     if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
  2560.     if (wmPtr->height >= 0) {
  2561.         hintsPtr->min_height = wmPtr->height;
  2562.     } else {
  2563.         hintsPtr->min_height = winPtr->reqHeight;
  2564.     }
  2565.     hintsPtr->max_height = hintsPtr->min_height;
  2566.     }
  2567.  
  2568.     XSetWMNormalHints(winPtr->display, winPtr->window, hintsPtr);
  2569.  
  2570.     XFree((char *) hintsPtr);
  2571. }
  2572.  
  2573. /*
  2574.  *----------------------------------------------------------------------
  2575.  *
  2576.  * WaitForConfigureNotify --
  2577.  *
  2578.  *    This procedure is invoked in order to synchronize with the
  2579.  *    window manager.  It waits for a ConfigureNotify event to
  2580.  *    arrive, signalling that the window manager has seen an attempt
  2581.  *    on our part to move or resize a top-level window.
  2582.  *
  2583.  * Results:
  2584.  *    None.
  2585.  *
  2586.  * Side effects:
  2587.  *    Delays the execution of the process until a ConfigureNotify event
  2588.  *    arrives with serial number at least as great as serial.  This
  2589.  *    is useful for two reasons:
  2590.  *
  2591.  *    1. It's important to distinguish ConfigureNotify events that are
  2592.  *       coming in response to a request we've made from those generated
  2593.  *       spontaneously by the user.  The reason for this is that if the
  2594.  *       user resizes the window we take that as an order to ignore
  2595.  *       geometry requests coming from inside the window hierarchy.  If
  2596.  *       we accidentally interpret a response to our request as a
  2597.  *       user-initiated action, the window will stop responding to
  2598.  *       new geometry requests.  To make this distinction, (a) this
  2599.  *       procedure sets a flag for TopLevelEventProc to indicate that
  2600.  *       we're waiting to sync with the wm, and (b) all changes to
  2601.  *       the size of a top-level window are followed by calls to this
  2602.  *       procedure.
  2603.  *    2. Races and confusion can come about if there are multiple
  2604.  *       operations outstanding at a time (e.g. two different resizes
  2605.  *       of the top-level window:  it's hard to tell which of the
  2606.  *       ConfigureNotify events coming back is for which request).
  2607.  *    While waiting, all events covered by StructureNotifyMask are
  2608.  *    processed and all others are deferred.
  2609.  *
  2610.  *----------------------------------------------------------------------
  2611.  */
  2612.  
  2613. static void
  2614. WaitForConfigureNotify(winPtr, serial)
  2615.     TkWindow *winPtr;        /* Top-level window for which we want
  2616.                  * to see a ConfigureNotify. */
  2617.     unsigned long serial;    /* Serial number of resize request.  Want to
  2618.                  * be sure wm has seen this. */
  2619. {
  2620.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  2621.     XEvent event;
  2622.     int diff;
  2623.     int gotConfig = 0;
  2624.  
  2625.     /*
  2626.      * One more tricky detail about this procedure.  In some cases the
  2627.      * window manager will decide to ignore a configure request (e.g.
  2628.      * because it thinks the window is already in the right place).
  2629.      * To avoid hanging in this situation, only wait for a few seconds,
  2630.      * then give up.
  2631.      */
  2632.  
  2633.     while (!gotConfig) {
  2634.     if (WaitForEvent(winPtr->display, winPtr->window, StructureNotifyMask,
  2635.         &event) != TCL_OK) {
  2636.         if (wmTracing) {
  2637.         printf("WaitForConfigureNotify giving up on %s\n",
  2638.             winPtr->pathName);
  2639.         }
  2640.         break;
  2641.     }
  2642.     wmPtr->flags |= WM_SYNC_PENDING;
  2643.     Tk_HandleEvent(&event);
  2644.     wmPtr->flags &= ~WM_SYNC_PENDING;
  2645.     if (event.type == ConfigureNotify) {
  2646.         diff = event.xconfigure.serial - serial;
  2647.         if (diff >= 0) {
  2648.         gotConfig = 1;
  2649.         }
  2650.     }
  2651.     }
  2652.     wmPtr->flags &= ~WM_MOVE_PENDING;
  2653.     if (wmTracing) {
  2654.     printf("WaitForConfigureNotify finished with %s, serial %ld\n",
  2655.         winPtr->pathName, serial);
  2656.     }
  2657. }
  2658.  
  2659. /*
  2660.  *----------------------------------------------------------------------
  2661.  *
  2662.  * WaitForEvent --
  2663.  *
  2664.  *    This procedure is used by WaitForConfigureNotify and
  2665.  *    WaitForMapNotify to wait for an event of a certain type
  2666.  *    to arrive.
  2667.  *
  2668.  * Results:
  2669.  *    Under normal conditions, TCL_OK is returned and an event for
  2670.  *    display and window that matches "mask" is stored in *eventPtr.
  2671.  *    If a long time goes by with no event of the right type arriving,
  2672.  *    or if an error occurs while waiting for the event to arrive,
  2673.  *    then TCL_ERROR is returned.
  2674.  *
  2675.  * Side effects:
  2676.  *    Events are removed from the display's event queue.  Time goes by.
  2677.  *
  2678.  *----------------------------------------------------------------------
  2679.  */
  2680.  
  2681. static int
  2682. WaitForEvent(display, window, mask, eventPtr)
  2683.     Display *display;        /* Display event is coming from. */
  2684.     Window window;        /* Window for which event is desired. */
  2685.     long mask;            /* Selects events of interest. */
  2686.     XEvent *eventPtr;        /* Place to store event. */
  2687. {
  2688. #define TIMEOUT_SECS 2
  2689.     fd_mask masks[MASK_SIZE];
  2690.     struct timeval timeout, startTime, curTime;
  2691.     int i, numFound, fd;
  2692.  
  2693.     for (i = 0; i < MASK_SIZE; i++) {
  2694.     masks[i] = 0;
  2695.     }
  2696.     fd = ConnectionNumber(display);
  2697.     (void) gettimeofday(&startTime, (struct timezone *) NULL);
  2698.     while (1) {
  2699.     if (XCheckWindowEvent(display, window, mask, eventPtr)) {
  2700.         return TCL_OK;
  2701.     }
  2702.     masks[fd/(NBBY*sizeof(fd_mask))] = 1 << fd%(NBBY*sizeof(fd_mask));
  2703.     timeout.tv_sec = 1;
  2704.     timeout.tv_usec = 0;
  2705.     numFound = select(fd+1, (SELECT_MASK *) masks,
  2706.         (SELECT_MASK *) NULL, (SELECT_MASK *) NULL,
  2707.         &timeout);
  2708.     if ((numFound == -1) && (errno != EINTR)) {
  2709.         return TCL_ERROR;
  2710.     }
  2711.     (void) gettimeofday(&curTime, (struct timezone *) NULL);
  2712.     i = curTime.tv_sec - startTime.tv_sec;
  2713.     if (i >= TIMEOUT_SECS) {
  2714.         return TCL_ERROR;
  2715.     }
  2716.     }
  2717. }
  2718.  
  2719. /*
  2720.  *----------------------------------------------------------------------
  2721.  *
  2722.  * WaitForMapNotify --
  2723.  *
  2724.  *    This procedure is invoked in order to synchronize with the
  2725.  *    window manager.  It waits for the window's mapped state to
  2726.  *    reach the value given by mapped.
  2727.  *
  2728.  * Results:
  2729.  *    None.
  2730.  *
  2731.  * Side effects:
  2732.  *    Delays the execution of the process until winPtr becomes mapped
  2733.  *    or unmapped, depending on the "mapped" argument.  This allows us
  2734.  *    to synchronize with the window manager, and allows us to
  2735.  *    identify changes in window size that come about when the window
  2736.  *    manager first starts managing the window (as opposed to those
  2737.  *    requested interactively by the user later).  See the comments
  2738.  *    for WaitForConfigureNotify and WM_SYNC_PENDING.  While waiting,
  2739.  *    all events covered by StructureNotifyMask are processed and all
  2740.  *    others are deferred.
  2741.  *
  2742.  *----------------------------------------------------------------------
  2743.  */
  2744.  
  2745. static void
  2746. WaitForMapNotify(winPtr, mapped)
  2747.     TkWindow *winPtr;        /* Top-level window for which we want
  2748.                  * to see a particular mapping state. */
  2749.     int mapped;            /* If non-zero, wait for window to become
  2750.                  * mapped, otherwise wait for it to become
  2751.                  * unmapped. */
  2752. {
  2753.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  2754.     XEvent event;
  2755.  
  2756.     while (1) {
  2757.     if (mapped) {
  2758.         if (winPtr->flags & TK_MAPPED) {
  2759.         break;
  2760.         }
  2761.     } else if (!(winPtr->flags & TK_MAPPED)) {
  2762.         break;
  2763.     }
  2764.     if (WaitForEvent(winPtr->display, winPtr->window, StructureNotifyMask,
  2765.         &event) != TCL_OK) {
  2766.         /*
  2767.          * There are some bizarre situations in which the window
  2768.          * manager can't respond or chooses not to (e.g. if we've
  2769.          * got a grab set it can't respond).  If this happens then
  2770.          * just quit.
  2771.          */
  2772.  
  2773.         if (wmTracing) {
  2774.         printf("WaitForMapNotify giving up on %s\n", winPtr->pathName);
  2775.         }
  2776.         break;
  2777.     }
  2778.     wmPtr->flags |= WM_SYNC_PENDING;
  2779.     Tk_HandleEvent(&event);
  2780.     wmPtr->flags &= ~WM_SYNC_PENDING;
  2781.     }
  2782.     wmPtr->flags &= ~WM_MOVE_PENDING;
  2783.     if (wmTracing) {
  2784.     printf("WaitForMapNotify finished with %s\n", winPtr->pathName);
  2785.     }
  2786. }
  2787.  
  2788. /*
  2789.  *--------------------------------------------------------------
  2790.  *
  2791.  * UpdateHints --
  2792.  *
  2793.  *    This procedure is called to update the window manager's
  2794.  *    hints information from the information in a WmInfo
  2795.  *    structure.
  2796.  *
  2797.  * Results:
  2798.  *    None.
  2799.  *
  2800.  * Side effects:
  2801.  *    Properties get changed for winPtr.
  2802.  *
  2803.  *--------------------------------------------------------------
  2804.  */
  2805.  
  2806. static void
  2807. UpdateHints(winPtr)
  2808.     TkWindow *winPtr;
  2809. {
  2810.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  2811.  
  2812.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  2813.     return;
  2814.     }
  2815.     XSetWMHints(winPtr->display, winPtr->window, &wmPtr->hints);
  2816. }
  2817.  
  2818. /*
  2819.  *--------------------------------------------------------------
  2820.  *
  2821.  * ParseGeometry --
  2822.  *
  2823.  *    This procedure parses a geometry string and updates
  2824.  *    information used to control the geometry of a top-level
  2825.  *    window.
  2826.  *
  2827.  * Results:
  2828.  *    A standard Tcl return value, plus an error message in
  2829.  *    interp->result if an error occurs.
  2830.  *
  2831.  * Side effects:
  2832.  *    The size and/or location of winPtr may change.
  2833.  *
  2834.  *--------------------------------------------------------------
  2835.  */
  2836.  
  2837. static int
  2838. ParseGeometry(interp, string, winPtr)
  2839.     Tcl_Interp *interp;        /* Used for error reporting. */
  2840.     char *string;        /* String containing new geometry.  Has the
  2841.                  * standard form "=wxh+x+y". */
  2842.     TkWindow *winPtr;        /* Pointer to top-level window whose
  2843.                  * geometry is to be changed. */
  2844. {
  2845.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2846.     int x, y, width, height, flags;
  2847.     char *end;
  2848.     register char *p = string;
  2849.  
  2850.     /*
  2851.      * The leading "=" is optional.
  2852.      */
  2853.  
  2854.     if (*p == '=') {
  2855.     p++;
  2856.     }
  2857.  
  2858.     /*
  2859.      * Parse the width and height, if they are present.  Don't
  2860.      * actually update any of the fields of wmPtr until we've
  2861.      * successfully parsed the entire geometry string.
  2862.      */
  2863.  
  2864.     width = wmPtr->width;
  2865.     height = wmPtr->height;
  2866.     x = wmPtr->x;
  2867.     y = wmPtr->y;
  2868.     flags = wmPtr->flags;
  2869.     if (isdigit(UCHAR(*p))) {
  2870.     width = strtoul(p, &end, 10);
  2871.     p = end;
  2872.     if (*p != 'x') {
  2873.         goto error;
  2874.     }
  2875.     p++;
  2876.     if (!isdigit(UCHAR(*p))) {
  2877.         goto error;
  2878.     }
  2879.     height = strtoul(p, &end, 10);
  2880.     p = end;
  2881.     }
  2882.  
  2883.     /*
  2884.      * Parse the X and Y coordinates, if they are present.
  2885.      */
  2886.  
  2887.     if (*p != '\0') {
  2888.     flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
  2889.     if (*p == '-') {
  2890.         flags |= WM_NEGATIVE_X;
  2891.     } else if (*p != '+') {
  2892.         goto error;
  2893.     }
  2894.     x = strtol(p+1, &end, 10);
  2895.     p = end;
  2896.     if (*p == '-') {
  2897.         flags |= WM_NEGATIVE_Y;
  2898.     } else if (*p != '+') {
  2899.         goto error;
  2900.     }
  2901.     y = strtol(p+1, &end, 10);
  2902.     if (*end != '\0') {
  2903.         goto error;
  2904.     }
  2905.  
  2906.     /*
  2907.      * Assume that the geometry information came from the user,
  2908.      * unless an explicit source has been specified.  Otherwise
  2909.      * most window managers assume that the size hints were
  2910.      * program-specified and they ignore them.
  2911.      */
  2912.  
  2913.     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
  2914.         wmPtr->sizeHintsFlags |= USPosition;
  2915.         flags |= WM_UPDATE_SIZE_HINTS;
  2916.     }
  2917.     }
  2918.  
  2919.     /*
  2920.      * Everything was parsed OK.  Update the fields of *wmPtr and
  2921.      * arrange for the appropriate information to be percolated out
  2922.      * to the window manager at the next idle moment.
  2923.      */
  2924.  
  2925.     wmPtr->width = width;
  2926.     wmPtr->height = height;
  2927.     wmPtr->x = x;
  2928.     wmPtr->y = y;
  2929.     flags |= WM_MOVE_PENDING;
  2930.     wmPtr->flags = flags;
  2931.  
  2932.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  2933.     Tk_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  2934.     wmPtr->flags |= WM_UPDATE_PENDING;
  2935.     }
  2936.     return TCL_OK;
  2937.  
  2938.     error:
  2939.     Tcl_AppendResult(interp, "bad geometry specifier \"",
  2940.         string, "\"", (char *) NULL);
  2941.     return TCL_ERROR;
  2942. }
  2943.  
  2944. /*
  2945.  *----------------------------------------------------------------------
  2946.  *
  2947.  * Tk_GetRootCoords --
  2948.  *
  2949.  *    Given a token for a window, this procedure traces through the
  2950.  *    window's lineage to find the (virtual) root-window coordinates
  2951.  *    corresponding to point (0,0) in the window.
  2952.  *
  2953.  * Results:
  2954.  *    The locations pointed to by xPtr and yPtr are filled in with
  2955.  *    the root coordinates of the (0,0) point in tkwin.  If a virtual
  2956.  *    root window is in effect for the window, then the coordinates
  2957.  *    in the virtual root are returned.
  2958.  *
  2959.  * Side effects:
  2960.  *    None.
  2961.  *
  2962.  *----------------------------------------------------------------------
  2963.  */
  2964.  
  2965. void
  2966. Tk_GetRootCoords(tkwin, xPtr, yPtr)
  2967.     Tk_Window tkwin;        /* Token for window. */
  2968.     int *xPtr;            /* Where to store x-displacement of (0,0). */
  2969.     int *yPtr;            /* Where to store y-displacement of (0,0). */
  2970. {
  2971.     int x, y;
  2972.     register TkWindow *winPtr = (TkWindow *) tkwin;
  2973.  
  2974.     /*
  2975.      * Search back through this window's parents all the way to a
  2976.      * top-level window, combining the offsets of each window within
  2977.      * its parent.
  2978.      */
  2979.  
  2980.     x = y = 0;
  2981.     while (1) {
  2982.     x += winPtr->changes.x + winPtr->changes.border_width;
  2983.     y += winPtr->changes.y + winPtr->changes.border_width;
  2984.     if ((winPtr->flags & TK_TOP_LEVEL) || (winPtr->parentPtr == NULL)) {
  2985.         break;
  2986.     }
  2987.     winPtr = winPtr->parentPtr;
  2988.     }
  2989.     *xPtr = x;
  2990.     *yPtr = y;
  2991. }
  2992.  
  2993. /*
  2994.  *----------------------------------------------------------------------
  2995.  *
  2996.  * Tk_CoordsToWindow --
  2997.  *
  2998.  *    Given the (virtual) root coordinates of a point, this procedure
  2999.  *    returns the token for the top-most window covering that point,
  3000.  *    if there exists such a window in this application.
  3001.  *
  3002.  * Results:
  3003.  *    The return result is either a token for the window corresponding
  3004.  *    to rootX and rootY, or else NULL to indicate that there is no such
  3005.  *    window.
  3006.  *
  3007.  * Side effects:
  3008.  *    None.
  3009.  *
  3010.  *----------------------------------------------------------------------
  3011.  */
  3012.  
  3013. Tk_Window
  3014. Tk_CoordsToWindow(rootX, rootY, tkwin)
  3015.     int rootX, rootY;        /* Coordinates of point in root window.  If
  3016.                  * a virtual-root window manager is in use,
  3017.                  * these coordinates refer to the virtual
  3018.                  * root, not the real root. */
  3019.     Tk_Window tkwin;        /* Token for any window in application;
  3020.                  * used to identify the display. */
  3021. {
  3022.     Window rootChild, root, vRoot;
  3023.     int dummy1, dummy2;
  3024.     register WmInfo *wmPtr;
  3025.     register TkWindow *winPtr, *childPtr;
  3026.     TkWindow *nextPtr;        /* Coordinates of highest child found so
  3027.                  * far that contains point. */
  3028.     int x, y;            /* Coordinates in winPtr. */
  3029.     int tmpx, tmpy, bd;
  3030.  
  3031.     /*
  3032.      * Step 1: find any top-level window for the right screen.
  3033.      */
  3034.  
  3035.     while (!Tk_IsTopLevel(tkwin)) {
  3036.     tkwin = Tk_Parent(tkwin);
  3037.     }
  3038.     wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
  3039.  
  3040.     /*
  3041.      * Step 2: find the window in the actual root that contains the
  3042.      * desired point.  Special trick:  if a virtual root window manager
  3043.      * is in use, there may be windows in both the true root (e.g.
  3044.      * pop-up menus) and in the virtual root;  have to look in *both*
  3045.      * places.
  3046.      */
  3047.  
  3048.     UpdateVRootGeometry(wmPtr);
  3049.     root = RootWindowOfScreen(Tk_Screen(tkwin));
  3050.     if (XTranslateCoordinates(Tk_Display(tkwin), root, root,
  3051.         rootX + wmPtr->vRootX, rootY + wmPtr->vRootY,
  3052.         &dummy1, &dummy2, &rootChild) == False) {
  3053.     panic("Tk_CoordsToWindow get False return from XTranslateCoordinates");
  3054.     }
  3055.  
  3056.     /*
  3057.      * Step 3: if the window we've found so far (a child of the root)
  3058.      * is the virtual root window, then look again to find the child of
  3059.      * the virtual root.
  3060.      */
  3061.  
  3062.     vRoot = ((TkWindow *) tkwin)->wmInfoPtr->vRoot;
  3063.     if ((vRoot != None) && (rootChild == vRoot)) {
  3064.     if (XTranslateCoordinates(Tk_Display(tkwin), vRoot, vRoot, rootX,
  3065.         rootY, &dummy1, &dummy2, &rootChild) == False) {
  3066.         panic("Tk_CoordsToWindow get False return from XTranslateCoordinates");
  3067.     }
  3068.     }
  3069.     for (wmPtr = firstWmPtr; ; wmPtr = wmPtr->nextPtr) {
  3070.     if (wmPtr == NULL) {
  3071.         return NULL;
  3072.     }
  3073.     if ((wmPtr->reparent == rootChild) || ((wmPtr->reparent == None)
  3074.         && (wmPtr->winPtr->window == rootChild))) {
  3075.         break;
  3076.     }
  3077.     }
  3078.     winPtr = wmPtr->winPtr;
  3079.     if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {
  3080.     return NULL;
  3081.     }
  3082.  
  3083.     /*
  3084.      * Step 4: work down through the hierarchy underneath this window.
  3085.      * At each level, scan through all the children to find the highest
  3086.      * one in the stacking order that contains the point.  Then repeat
  3087.      * the whole process on that child.
  3088.      */
  3089.  
  3090.     x = rootX;
  3091.     y = rootY;
  3092.     while (1) {
  3093.     x -= winPtr->changes.x;
  3094.     y -= winPtr->changes.y;
  3095.     nextPtr = NULL;
  3096.     for (childPtr = winPtr->childList; childPtr != NULL;
  3097.         childPtr = childPtr->nextPtr) {
  3098.         if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) {
  3099.         continue;
  3100.         }
  3101.         tmpx = x - childPtr->changes.x;
  3102.         tmpy = y - childPtr->changes.y;
  3103.         bd = childPtr->changes.border_width;
  3104.         if ((tmpx >= -bd) && (tmpy >= -bd)
  3105.             && (tmpx < (childPtr->changes.width + bd))
  3106.             && (tmpy < (childPtr->changes.height + bd))) {
  3107.         nextPtr = childPtr;
  3108.         }
  3109.     }
  3110.     if (nextPtr == NULL) {
  3111.         break;
  3112.     }
  3113.     winPtr = nextPtr;
  3114.     }
  3115.     return (Tk_Window) winPtr;
  3116. }
  3117.  
  3118. /*
  3119.  *----------------------------------------------------------------------
  3120.  *
  3121.  * UpdateVRootGeometry --
  3122.  *
  3123.  *    This procedure is called to update all the virtual root
  3124.  *    geometry information in wmPtr.
  3125.  *
  3126.  * Results:
  3127.  *    None.
  3128.  *
  3129.  * Side effects:
  3130.  *    The vRootX, vRootY, vRootWidth, and vRootHeight fields in
  3131.  *    wmPtr are filled with the most up-to-date information.
  3132.  *
  3133.  *----------------------------------------------------------------------
  3134.  */
  3135.  
  3136. static void
  3137. UpdateVRootGeometry(wmPtr)
  3138.     WmInfo *wmPtr;        /* Window manager information to be
  3139.                  * updated.  The wmPtr->vRoot field must
  3140.                  * be valid. */
  3141. {
  3142.     TkWindow *winPtr = wmPtr->winPtr;
  3143.     int bd;
  3144.     unsigned int dummy;
  3145.     Window dummy2;
  3146.     Status status;
  3147.     Tk_ErrorHandler handler;
  3148.  
  3149.     /*
  3150.      * If this isn't a virtual-root window manager, just return information
  3151.      * about the screen.
  3152.      */
  3153.  
  3154.     wmPtr->flags &= ~WM_VROOT_OFFSET_STALE;
  3155.     if (wmPtr->vRoot == None) {
  3156.     noVRoot:
  3157.     wmPtr->vRootX = wmPtr->vRootY = 0;
  3158.     wmPtr->vRootWidth = DisplayWidth(winPtr->display, winPtr->screenNum);
  3159.     wmPtr->vRootHeight = DisplayHeight(winPtr->display, winPtr->screenNum);
  3160.     return;
  3161.     }
  3162.  
  3163.     /*
  3164.      * Refresh the virtual root information if it's out of date.
  3165.      */
  3166.  
  3167.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  3168.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  3169.     status = XGetGeometry(winPtr->display, wmPtr->vRoot,
  3170.         &dummy2, &wmPtr->vRootX, &wmPtr->vRootY,
  3171.         (unsigned int *) &wmPtr->vRootWidth,
  3172.         (unsigned int *) &wmPtr->vRootHeight, (unsigned int *) &bd,
  3173.         &dummy);
  3174.     if (wmTracing) {
  3175.     printf("UpdateVRootGeometry: x = %d, y = %d, width = %d, ",
  3176.         wmPtr->vRootX, wmPtr->vRootY, wmPtr->vRootWidth);
  3177.     printf("height = %d, status = %d\n", wmPtr->vRootHeight, status);
  3178.     }
  3179.     Tk_DeleteErrorHandler(handler);
  3180.     if (status == 0) {
  3181.     /*
  3182.      * The virtual root is gone!  Pretend that it never existed.
  3183.      */
  3184.  
  3185.     wmPtr->vRoot = None;
  3186.     goto noVRoot;
  3187.     }
  3188. }
  3189.  
  3190. /*
  3191.  *----------------------------------------------------------------------
  3192.  *
  3193.  * Tk_GetVRootGeometry --
  3194.  *
  3195.  *    This procedure returns information about the virtual root
  3196.  *    window corresponding to a particular Tk window.
  3197.  *
  3198.  * Results:
  3199.  *    The values at xPtr, yPtr, widthPtr, and heightPtr are set
  3200.  *    with the offset and dimensions of the root window corresponding
  3201.  *    to tkwin.  If tkwin is being managed by a virtual root window
  3202.  *    manager these values correspond to the virtual root window being
  3203.  *    used for tkwin;  otherwise the offsets will be 0 and the
  3204.  *    dimensions will be those of the screen.
  3205.  *
  3206.  * Side effects:
  3207.  *    Vroot window information is refreshed if it is out of date.
  3208.  *
  3209.  *----------------------------------------------------------------------
  3210.  */
  3211.  
  3212. void
  3213. Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)
  3214.     Tk_Window tkwin;        /* Window whose virtual root is to be
  3215.                  * queried. */
  3216.     int *xPtr, *yPtr;        /* Store x and y offsets of virtual root
  3217.                  * here. */
  3218.     int *widthPtr, *heightPtr;    /* Store dimensions of virtual root here. */
  3219. {
  3220.     WmInfo *wmPtr;
  3221.     TkWindow *winPtr = (TkWindow *) tkwin;
  3222.  
  3223.     /*
  3224.      * Find the top-level window for tkwin, and locate the window manager
  3225.      * information for that window.
  3226.      */
  3227.  
  3228.     while (!(winPtr->flags & TK_TOP_LEVEL) && (winPtr->parentPtr != NULL)) {
  3229.     winPtr = winPtr->parentPtr;
  3230.     }
  3231.     wmPtr = winPtr->wmInfoPtr;
  3232.  
  3233.     /*
  3234.      * Make sure that the geometry information is up-to-date, then copy
  3235.      * it out to the caller.
  3236.      */
  3237.  
  3238.     if (wmPtr->flags & WM_VROOT_OFFSET_STALE) {
  3239.     UpdateVRootGeometry(wmPtr);
  3240.     }
  3241.     *xPtr = wmPtr->vRootX;
  3242.     *yPtr = wmPtr->vRootY;
  3243.     *widthPtr = wmPtr->vRootWidth;
  3244.     *heightPtr = wmPtr->vRootHeight;
  3245. }
  3246.  
  3247. /*
  3248.  *----------------------------------------------------------------------
  3249.  *
  3250.  * Tk_MoveToplevelWindow --
  3251.  *
  3252.  *    This procedure is called instead of Tk_MoveWindow to adjust
  3253.  *    the x-y location of a top-level window.  It delays the actual
  3254.  *    move to a later time and keeps window-manager information
  3255.  *    up-to-date with the move
  3256.  *
  3257.  * Results:
  3258.  *    None.
  3259.  *
  3260.  * Side effects:
  3261.  *    The window is eventually moved so that its upper-left corner
  3262.  *    (actually, the upper-left corner of the window's decorative
  3263.  *    frame, if there is one) is at (x,y).
  3264.  *
  3265.  *----------------------------------------------------------------------
  3266.  */
  3267.  
  3268. void
  3269. Tk_MoveToplevelWindow(tkwin, x, y)
  3270.     Tk_Window tkwin;        /* Window to move. */
  3271.     int x, y;            /* New location for window (within
  3272.                  * parent). */
  3273. {
  3274.     TkWindow *winPtr = (TkWindow *) tkwin;
  3275.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  3276.  
  3277.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  3278.     panic("Tk_MoveToplevelWindow called with non-toplevel window");
  3279.     }
  3280.     wmPtr->x = x;
  3281.     wmPtr->y = y;
  3282.     wmPtr->flags |= WM_MOVE_PENDING;
  3283.     wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
  3284.     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
  3285.     wmPtr->sizeHintsFlags |= USPosition;
  3286.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  3287.     }
  3288.  
  3289.     /*
  3290.      * If the window has already been mapped, must bring its geometry
  3291.      * up-to-date immediately, otherwise an event might arrive from the
  3292.      * server that would overwrite wmPtr->x and wmPtr->y and lose the
  3293.      * new position.
  3294.      */
  3295.  
  3296.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  3297.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  3298.         Tk_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  3299.     }
  3300.     UpdateGeometryInfo((ClientData) winPtr);
  3301.     }
  3302. }
  3303.  
  3304. /*
  3305.  *----------------------------------------------------------------------
  3306.  *
  3307.  * UpdateWmProtocols --
  3308.  *
  3309.  *    This procedure transfers the most up-to-date information about
  3310.  *    window manager protocols from the WmInfo structure to the actual
  3311.  *    property on the top-level window.
  3312.  *
  3313.  * Results:
  3314.  *    None.
  3315.  *
  3316.  * Side effects:
  3317.  *    The WM_PROTOCOLS property gets changed for wmPtr's window.
  3318.  *
  3319.  *----------------------------------------------------------------------
  3320.  */
  3321.  
  3322. static void
  3323. UpdateWmProtocols(wmPtr)
  3324.     register WmInfo *wmPtr;    /* Information about top-level window. */
  3325. {
  3326.     register ProtocolHandler *protPtr;
  3327.     Atom deleteWindowAtom;
  3328.     int count;
  3329.     Atom *arrayPtr, *atomPtr;
  3330.  
  3331.     /*
  3332.      * There are only two tricky parts here.  First, there could be any
  3333.      * number of atoms for the window, so count them and malloc an array
  3334.      * to hold all of their atoms.  Second, we *always* want to respond
  3335.      * to the WM_DELETE_WINDOW protocol, even if no-one's officially asked.
  3336.      */
  3337.  
  3338.     for (protPtr = wmPtr->protPtr, count = 1; protPtr != NULL;
  3339.         protPtr = protPtr->nextPtr, count++) {
  3340.     /* Empty loop body;  we're just counting the handlers. */
  3341.     }
  3342.     arrayPtr = (Atom *) ckalloc((unsigned) (count * sizeof(Atom)));
  3343.     deleteWindowAtom = Tk_InternAtom((Tk_Window) wmPtr->winPtr,
  3344.         "WM_DELETE_WINDOW");
  3345.     arrayPtr[0] = deleteWindowAtom;
  3346.     for (protPtr = wmPtr->protPtr, atomPtr = &arrayPtr[1];
  3347.         protPtr != NULL; protPtr = protPtr->nextPtr) {
  3348.     if (protPtr->protocol != deleteWindowAtom) {
  3349.         *atomPtr = protPtr->protocol;
  3350.         atomPtr++;
  3351.     }
  3352.     }
  3353.     XChangeProperty(wmPtr->winPtr->display, wmPtr->winPtr->window,
  3354.         Tk_InternAtom((Tk_Window) wmPtr->winPtr, "WM_PROTOCOLS"),
  3355.         XA_ATOM, 32, PropModeReplace, (unsigned char *) arrayPtr,
  3356.         atomPtr-arrayPtr);
  3357.     ckfree((char *) arrayPtr);
  3358. }
  3359.  
  3360. /*
  3361.  *----------------------------------------------------------------------
  3362.  *
  3363.  * TkWmProtocolEventProc --
  3364.  *
  3365.  *    This procedure is called by the Tk_HandleEvent whenever a
  3366.  *    ClientMessage event arrives whose type is "WM_PROTOCOLS".
  3367.  *    This procedure handles the message from the window manager
  3368.  *    in an appropriate fashion.
  3369.  *
  3370.  * Results:
  3371.  *    None.
  3372.  *
  3373.  * Side effects:
  3374.  *    Depends on what sort of handler, if any, was set up for the
  3375.  *    protocol.
  3376.  *
  3377.  *----------------------------------------------------------------------
  3378.  */
  3379.  
  3380. void
  3381. TkWmProtocolEventProc(winPtr, eventPtr)
  3382.     TkWindow *winPtr;        /* Window to which the event was sent. */
  3383.     XEvent *eventPtr;        /* X event. */
  3384. {
  3385.     WmInfo *wmPtr;
  3386.     register ProtocolHandler *protPtr;
  3387.     Atom protocol;
  3388.     int result;
  3389.  
  3390.     wmPtr = winPtr->wmInfoPtr;
  3391.     if (wmPtr == NULL) {
  3392.     return;
  3393.     }
  3394.     protocol = (Atom) eventPtr->xclient.data.l[0];
  3395.     for (protPtr = wmPtr->protPtr; protPtr != NULL;
  3396.         protPtr = protPtr->nextPtr) {
  3397.     if (protocol == protPtr->protocol) {
  3398.         Tk_Preserve((ClientData) protPtr);
  3399.         result = Tcl_GlobalEval(protPtr->interp, protPtr->command);
  3400.         if (result != TCL_OK) {
  3401.         Tcl_AddErrorInfo(protPtr->interp, "\n    (command for \"");
  3402.         Tcl_AddErrorInfo(protPtr->interp,
  3403.             Tk_GetAtomName((Tk_Window) winPtr, protocol));
  3404.         Tcl_AddErrorInfo(protPtr->interp,
  3405.             "\" window manager protocol)");
  3406.         Tk_BackgroundError(protPtr->interp);
  3407.         }
  3408.         Tk_Release((ClientData) protPtr);
  3409.         return;
  3410.     }
  3411.     }
  3412.  
  3413.     /*
  3414.      * No handler was present for this protocol.  If this is a
  3415.      * WM_DELETE_WINDOW message then just destroy the window.
  3416.      */
  3417.  
  3418.     if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
  3419.     Tk_DestroyWindow((Tk_Window) winPtr);
  3420.     }
  3421. }
  3422.  
  3423. /*
  3424.  *----------------------------------------------------------------------
  3425.  *
  3426.  * TkWmRestackToplevel --
  3427.  *
  3428.  *    This procedure restacks a top-level window.
  3429.  *
  3430.  * Results:
  3431.  *    None.
  3432.  *
  3433.  * Side effects:
  3434.  *    WinPtr gets restacked  as specified by aboveBelow and otherPtr.
  3435.  *    This procedure doesn't return until the restack has taken
  3436.  *    effect and the ConfigureNotify event for it has been received.
  3437.  *
  3438.  *----------------------------------------------------------------------
  3439.  */
  3440.  
  3441. void
  3442. TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
  3443.     TkWindow *winPtr;        /* Window to restack. */
  3444.     int aboveBelow;        /* Gives relative position for restacking;
  3445.                  * must be Above or Below. */
  3446.     TkWindow *otherPtr;        /* Window relative to which to restack;
  3447.                  * if NULL, then winPtr gets restacked
  3448.                  * above or below *all* siblings. */
  3449. {
  3450.     XWindowChanges changes;
  3451.     XWindowAttributes atts;
  3452.     unsigned int mask;
  3453.     Window window, dummy1, dummy2, vRoot;
  3454.     Window *children;
  3455.     unsigned int numChildren;
  3456.     int i;
  3457.     int desiredIndex = 0;    /* Initialized to stop gcc warnings. */
  3458.     int ourIndex = 0;        /* Initialized to stop gcc warnings. */
  3459.     unsigned long serial;
  3460.     XEvent event;
  3461.     int diff;
  3462.     Tk_ErrorHandler handler;
  3463.  
  3464.     changes.stack_mode = aboveBelow;
  3465.     changes.sibling = None;
  3466.     mask = CWStackMode;
  3467.     if (winPtr->window == None) {
  3468.     Tk_MakeWindowExist((Tk_Window) winPtr);
  3469.     }
  3470.     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  3471.     /*
  3472.      * Can't set stacking order properly until the window is on the
  3473.      * screen (mapping it may give it a reparent window), so make sure
  3474.      * it's on the screen.
  3475.      */
  3476.  
  3477.     TkWmMapWindow(winPtr);
  3478.     }
  3479.     window = (winPtr->wmInfoPtr->reparent != None)
  3480.         ? winPtr->wmInfoPtr->reparent : winPtr->window;
  3481.     if (otherPtr != NULL) {
  3482.     if (otherPtr->window == None) {
  3483.         Tk_MakeWindowExist((Tk_Window) otherPtr);
  3484.     }
  3485.     if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  3486.         TkWmMapWindow(otherPtr);
  3487.     }
  3488.     changes.sibling = (otherPtr->wmInfoPtr->reparent != None)
  3489.         ? otherPtr->wmInfoPtr->reparent : otherPtr->window;
  3490.     mask = CWStackMode|CWSibling;
  3491.     }
  3492.  
  3493.     /*
  3494.      * Before actually reconfiguring the window, see if it's already
  3495.      * in the right place.  If so then don't reconfigure it.  The
  3496.      * reason for this extra work is that some window managers will
  3497.      * ignore the reconfigure request if the window is already in
  3498.      * the right place, causing a long delay in WaitForConfigureNotify
  3499.      * while it times out.  Special note: if the window is almost in
  3500.      * the right place, and the only windows between it and the right
  3501.      * place aren't mapped, then we don't reconfigure it either, for
  3502.      * the same reason.
  3503.      */
  3504.  
  3505.     vRoot = winPtr->wmInfoPtr->vRoot;
  3506.     if (vRoot == None) {
  3507.     vRoot = RootWindowOfScreen(Tk_Screen((Tk_Window) winPtr));
  3508.     }
  3509.     if (XQueryTree(winPtr->display, vRoot, &dummy1, &dummy2,
  3510.         &children, &numChildren) != 0) {
  3511.     /*
  3512.      * Find where our window is in the stacking order, and
  3513.      * compute the desired location in the stacking order.
  3514.      */
  3515.  
  3516.     for (i = 0; i < numChildren; i++) {
  3517.         if (children[i] == window) {
  3518.         ourIndex = i;
  3519.         }
  3520.         if (children[i] == changes.sibling) {
  3521.         desiredIndex = i;
  3522.         }
  3523.     }
  3524.     if (mask & CWSibling) {
  3525.         if (aboveBelow == Above) {
  3526.         if (desiredIndex < ourIndex) {
  3527.             desiredIndex += 1;
  3528.         }
  3529.         } else {
  3530.         if (desiredIndex > ourIndex) {
  3531.             desiredIndex -= 1;
  3532.         }
  3533.         }
  3534.     } else {
  3535.         if (aboveBelow == Above) {
  3536.         desiredIndex = numChildren-1;
  3537.         } else {
  3538.         desiredIndex = 0;
  3539.         }
  3540.     }
  3541.  
  3542.     /*
  3543.      * See if there are any mapped windows between where we are
  3544.      * and where we want to be.
  3545.      */
  3546.  
  3547.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  3548.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  3549.     while (desiredIndex != ourIndex) {
  3550.         if ((XGetWindowAttributes(winPtr->display, children[desiredIndex],
  3551.             &atts) != 0) && (atts.map_state != IsUnmapped)) {
  3552.         break;
  3553.         }
  3554.         if (desiredIndex < ourIndex) {
  3555.         desiredIndex++;
  3556.         } else {
  3557.         desiredIndex--;
  3558.         }
  3559.     }
  3560.     Tk_DeleteErrorHandler(handler);
  3561.     XFree((char *) children);
  3562.     if (ourIndex == desiredIndex) {
  3563.         return;
  3564.     }
  3565.     }
  3566.  
  3567.     /*
  3568.      * Reconfigure the window.  This tricky because of two things:
  3569.      * (a) Some window managers, like olvwm, insist that we raise
  3570.      *     or lower the toplevel window itself, as opposed to its
  3571.      *     decorative frame.  Attempts to raise or lower the frame
  3572.      *     are ignored.
  3573.      * (b) If the raise or lower is relative to a sibling, X will
  3574.      *     generate an error unless we work with the frames (the
  3575.      *     toplevels themselves aren't siblings).
  3576.      * The only solution I can see is to switch to using the window
  3577.      * itself instead of its frame, if there's no sibling, but use
  3578.      * the frames if there's a sibling.  This means that raising
  3579.      * relative to a sibling won't work under olvwm.
  3580.      */
  3581.  
  3582.     if (!(mask & CWSibling)) {
  3583.     window = winPtr->window;
  3584.     }
  3585.     serial = NextRequest(winPtr->display);
  3586.     XConfigureWindow(winPtr->display, window, mask, &changes);
  3587.  
  3588.     /*
  3589.      * Wait for the reconfiguration to complete.  If we don't wait, then
  3590.      * the window may not restack for a while and the application might
  3591.      * observe it before it has restacked.  Waiting for the reconfiguration
  3592.      * is tricky if winPtr has been reparented, since the window getting
  3593.      * the event isn't one that Tk owns.
  3594.      */
  3595.  
  3596.     if (window == winPtr->window) {
  3597.     WaitForConfigureNotify(winPtr, serial);
  3598.     } else {
  3599.     XSelectInput(winPtr->display, window, StructureNotifyMask);
  3600.     while (1) {
  3601.         if (WaitForEvent(winPtr->display, window, StructureNotifyMask,
  3602.             &event) != TCL_OK) {
  3603.         break;
  3604.         }
  3605.         if ((event.type == ConfigureNotify)
  3606.             && (event.xconfigure.window == window)) {
  3607.         diff = event.xconfigure.serial - serial;
  3608.         if (diff >= 0) {
  3609.             break;
  3610.         }
  3611.         }
  3612.     }
  3613.     XSelectInput(winPtr->display, window, (long) 0);
  3614.     }
  3615. }
  3616.  
  3617. /*
  3618.  *----------------------------------------------------------------------
  3619.  *
  3620.  * TkWmAddToColormapWindows --
  3621.  *
  3622.  *    This procedure is called to add a given window to the
  3623.  *    WM_COLORMAP_WINDOWS property for its top-level, if it
  3624.  *    isn't already there.  It is invoked by the Tk code that
  3625.  *    creates a new colormap, in order to make sure that colormap
  3626.  *    information is propagated to the window manager by default.
  3627.  *
  3628.  * Results:
  3629.  *    None.
  3630.  *
  3631.  * Side effects:
  3632.  *    WinPtr's window gets added to the WM_COLORMAP_WINDOWS
  3633.  *    property of its nearest top-level ancestor, unless the
  3634.  *    colormaps have been set explicitly with the
  3635.  *    "wm colormapwindows" command.
  3636.  *
  3637.  *----------------------------------------------------------------------
  3638.  */
  3639.  
  3640. void
  3641. TkWmAddToColormapWindows(winPtr)
  3642.     TkWindow *winPtr;        /* Window with a non-default colormap.
  3643.                  * Should not be a top-level window. */
  3644. {
  3645.     TkWindow *topPtr;
  3646.     Window *oldPtr, *newPtr;
  3647.     int count, i;
  3648.  
  3649.     if (winPtr->window == None) {
  3650.     return;
  3651.     }
  3652.  
  3653.     for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
  3654.     if (topPtr == NULL) {
  3655.         /*
  3656.          * Window is being deleted.  Skip the whole operation.
  3657.          */
  3658.  
  3659.         return;
  3660.     }
  3661.     if (topPtr->flags & TK_TOP_LEVEL) {
  3662.         break;
  3663.     }
  3664.     }
  3665.     if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
  3666.     return;
  3667.     }
  3668.  
  3669.     /*
  3670.      * Fetch the old value of the property.
  3671.      */
  3672.  
  3673.     if (XGetWMColormapWindows(topPtr->display, topPtr->window,
  3674.         &oldPtr, &count) == 0) {
  3675.     oldPtr = NULL;
  3676.     count = 0;
  3677.     }
  3678.  
  3679.     /*
  3680.      * Make sure that the window isn't already in the list.
  3681.      */
  3682.  
  3683.     for (i = 0; i < count; i++) {
  3684.     if (oldPtr[i] == winPtr->window) {
  3685.         return;
  3686.     }
  3687.     }
  3688.  
  3689.     /*
  3690.      * Make a new bigger array and use it to reset the property.
  3691.      * Automatically add the toplevel itself as the last element
  3692.      * of the list.
  3693.      */
  3694.  
  3695.     newPtr = (Window *) ckalloc((unsigned) ((count+2)*sizeof(Window)));
  3696.     for (i = 0; i < count; i++) {
  3697.     newPtr[i] = oldPtr[i];
  3698.     }
  3699.     if (count == 0) {
  3700.     count++;
  3701.     }
  3702.     newPtr[count-1] = winPtr->window;
  3703.     newPtr[count] = topPtr->window;
  3704.     XSetWMColormapWindows(topPtr->display, topPtr->window, newPtr, count+1);
  3705.     ckfree((char *) newPtr);
  3706.     if (oldPtr != NULL) {
  3707.     XFree((char *) oldPtr);
  3708.     }
  3709. }
  3710.  
  3711. /*
  3712.  *----------------------------------------------------------------------
  3713.  *
  3714.  * TkGetPointerCoords --
  3715.  *
  3716.  *    Fetch the position of the mouse pointer.
  3717.  *
  3718.  * Results:
  3719.  *    *xPtr and *yPtr are filled in with the (virtual) root coordinates
  3720.  *    of the mouse pointer for tkwin's display.  If the pointer isn't
  3721.  *    on tkwin's screen, then -1 values are returned for both
  3722.  *    coordinates.
  3723.  *
  3724.  * Side effects:
  3725.  *    None.
  3726.  *
  3727.  *----------------------------------------------------------------------
  3728.  */
  3729.  
  3730. void
  3731. TkGetPointerCoords(tkwin, xPtr, yPtr)
  3732.     Tk_Window tkwin;        /* Window that identifies screen on which
  3733.                  * lookup is to be done. */
  3734.     int *xPtr, *yPtr;        /* Store pointer coordinates here. */
  3735. {
  3736.     TkWindow *winPtr = (TkWindow *) tkwin;
  3737.     WmInfo *wmPtr;
  3738.     Window w, root, child;
  3739.     int rootX, rootY;
  3740.     unsigned int mask;
  3741.  
  3742.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  3743.     winPtr = winPtr->parentPtr;
  3744.     if (winPtr == NULL) {
  3745.         *xPtr = -1;
  3746.         *yPtr = -1;
  3747.         return;
  3748.     }
  3749.     }
  3750.     wmPtr = winPtr->wmInfoPtr;
  3751.  
  3752.     w = wmPtr->vRoot;
  3753.     if (w == None) {
  3754.     w = RootWindow(winPtr->display, winPtr->screenNum);
  3755.     }
  3756.     if (XQueryPointer(winPtr->display, w, &root, &child, &rootX, &rootY,
  3757.         xPtr, yPtr, &mask) != True) {
  3758.     *xPtr = -1;
  3759.     *yPtr = -1;
  3760.     }
  3761. }
  3762.